Skip to content

Commit 7375d13

Browse files
committed
Merge branch 'fix/7bit/killerjulian' into development
2 parents ff84185 + f4bcaf1 commit 7375d13

4 files changed

Lines changed: 87 additions & 65 deletions

File tree

src/utils/Data/Data.ts

Lines changed: 21 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -151,9 +151,9 @@ export class Data {
151151
headerSize++;
152152
}
153153

154-
const parts = this.splitMessage(max, headerSize);
154+
const parts = this.splitMessage(max - headerSize);
155155
const haveHeader = parts.length > 1;
156-
const uniqID = Math.floor(Math.random() * 0xffff);
156+
const uniqID = (Math.random() * 0x10000) | 0;
157157

158158
// message will be splited, need headers
159159
if (haveHeader) {
@@ -183,10 +183,18 @@ export class Data {
183183
const data = tmp.result;
184184

185185
if (haveHeader) {
186-
size += headerSize;
186+
if (pdu.dataCodingScheme.textAlphabet === DCS.ALPHABET_DEFAULT) {
187+
// When using 7bit encoding (ALPHABET_DEFAULT), the UDH must be padded into septets.
188+
// 1 byte = 8 bits, so we calculate the UDH size in septets: ceil(bytes * 8 / 7)
189+
// This ensures the user data length is correct and no character is lost.
190+
size += Math.ceil((headerSize * 8) / 7);
191+
} else {
192+
// For 8bit and UCS2, header size is already in bytes and can be added directly.
193+
size += headerSize;
194+
}
187195
}
188196

189-
this._parts.push(new Part(data, size, '', header));
197+
this._parts.push(new Part(data, size, text, header));
190198
});
191199
}
192200

@@ -209,32 +217,18 @@ export class Data {
209217
}
210218

211219
private sortParts() {
212-
this._parts.sort((p1, p2) => {
213-
const index1 = p1.header?.getCurrent() || 1;
214-
const index2 = p2.header?.getCurrent() || 1;
215-
216-
return index1 > index2 ? 1 : -1;
217-
});
218-
219-
this._data = this._parts.map((part) => part.text).join('');
220+
this._data = this._parts
221+
.sort((p1, p2) => (p1.header?.getCurrent() || 1) - (p2.header?.getCurrent() || 1))
222+
.map((part) => part.text)
223+
.join('');
220224
}
221225

222-
private splitMessage(max: number, headerSize = Data.HEADER_SIZE) {
223-
// size less or equal max
224-
if (this.size <= max) {
225-
return [this._data];
226-
}
227-
228-
// parts of message
226+
private splitMessage(size: number) {
229227
const data = [];
230-
const size = max - headerSize;
231-
let offset = 0;
232-
233-
do {
234-
const part = this._data.substring(offset, offset + size);
235-
data.push(part);
236-
offset += size;
237-
} while (offset < this.size);
228+
229+
for (let i = 0; i < this._data.length; i += size) {
230+
data.push(this._data.substring(i, i + size));
231+
}
238232

239233
return data;
240234
}

src/utils/Data/Part.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,6 @@ export class Part {
6161
*/
6262
toString(pdu: Deliver | Submit) {
6363
// concate pdu, size of part, headers, data
64-
return this.getPduString(pdu) + this.getPartSize() + (this.header || '') + this.data;
64+
return this.getPduString(pdu) + this.getPartSize() + (!!this.header ? this.header.toString() : '') + this.data;
6565
}
6666
}

src/utils/Helper.ts

Lines changed: 35 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,10 @@ export class Helper {
8787
static decode7Bit(text: string, inLen?: number, alignBits?: number) {
8888
const ret: number[] = [];
8989
const data = Buffer.from(text, 'hex');
90+
9091
let dataPos = 0; // Position in the input octets stream
9192
let buf = 0; // Bit buffer, used in FIFO manner
92-
let bufLen = 0; // Ammount of buffered bits
93+
let bufLen = 0; // Amount of buffered bits
9394
let inDone = 0;
9495
let inExt = false;
9596

@@ -113,22 +114,18 @@ export class Helper {
113114
}
114115

115116
// Fetch next septet from the FIFO buffer
116-
let digit = buf & 0x7f;
117+
const digit = buf & 0x7f;
118+
117119
buf >>= 7;
118120
bufLen -= 7;
119121
inDone++;
120122

121123
if (digit % 128 === 27) {
124+
// Escape character
122125
inExt = true;
123126
} else {
124-
let c;
125-
126-
if (inExt) {
127-
c = Helper.EXTENDED_TABLE.charCodeAt(digit);
128-
inExt = false;
129-
} else {
130-
c = Helper.ALPHABET_7BIT.charCodeAt(digit);
131-
}
127+
let c = inExt ? Helper.EXTENDED_TABLE.charCodeAt(digit) || 63 : Helper.ALPHABET_7BIT.charCodeAt(digit);
128+
inExt = false;
132129

133130
if (c < 0x80) {
134131
ret.push(c);
@@ -140,7 +137,7 @@ export class Helper {
140137
(Helper.EXTENDED_TABLE.charCodeAt(digit + 1) & 0xfc00) === 0xdc00
141138
) {
142139
// Surrogate Pair
143-
c = 0x10000 + ((c & 0x03ff) << 10) + (Helper.EXTENDED_TABLE.charCodeAt(++digit) & 0x03ff);
140+
c = 0x10000 + ((c & 0x03ff) << 10) + (Helper.EXTENDED_TABLE.charCodeAt(digit + 1) & 0x03ff);
144141
ret.push(0xf0 | (c >> 18), 0x80 | ((c >> 12) & 0x3f), 0x80 | ((c >> 6) & 0x3f), 0x80 | (c & 0x3f));
145142
} else {
146143
ret.push(0xe0 | (c >> 12), 0x80 | ((c >> 6) & 0x3f), 0x80 | (c & 0x3f));
@@ -175,7 +172,7 @@ export class Helper {
175172
const buffer = Buffer.from(text, 'ascii');
176173

177174
for (let i = 0; i < buffer.length; i++) {
178-
pdu += this.toStringHex(buffer[i]);
175+
pdu += Helper.toStringHex(buffer[i]);
179176
length++;
180177
}
181178

@@ -190,50 +187,57 @@ export class Helper {
190187
*
191188
* @returns An object containing the length of the encoded text in septets and the result as a hexadecimal string
192189
*/
193-
static encode7Bit(text: string, alignBits?: number) {
194-
let ret = '';
190+
static encode7Bit(text: string, alignBits = 0) {
191+
let result = '';
195192
let buf = 0; // Bit buffer, used in FIFO manner
196-
let bufLen = 0; // Ammount of buffered bits
197-
let length = 0; // Ammount of produced septets
193+
let bufLen = 0; // Amount of buffered bits
194+
let length = 0; // Amount of produced septets
198195

199-
// Insert leading alignment zero bits if requested
200-
if (alignBits) {
201-
bufLen += alignBits;
202-
}
196+
// Adjust for initial padding if alignBits is specified
197+
bufLen = alignBits;
203198

204199
for (const symb of text) {
205-
let code;
200+
let code: number;
206201

207202
if ((code = Helper.ALPHABET_7BIT.indexOf(symb)) !== -1) {
203+
// Normal character
208204
buf |= code << bufLen;
209205
bufLen += 7;
210206
length++;
211207
} else if ((code = Helper.EXTENDED_TABLE.indexOf(symb)) !== -1) {
212-
buf |= ((code << 7) | 27) << bufLen;
213-
bufLen += 14;
214-
length += 2;
208+
// ESC character (27), then the actual extended character
209+
buf |= 27 << bufLen;
210+
bufLen += 7;
211+
length++;
212+
213+
// Then add extended character
214+
buf |= code << bufLen;
215+
bufLen += 7;
216+
length++;
215217
} else {
216-
buf |= 37 << bufLen; // Place space symbol
218+
// Replace unknown with space (' '- code 0x20)
219+
buf |= 32 << bufLen;
217220
bufLen += 7;
218221
length++;
219222
}
220223

221224
while (bufLen >= 8) {
222-
ret += this.toStringHex(buf & 0xff);
225+
result += Helper.toStringHex(buf & 0xff);
223226
buf >>= 8;
224227
bufLen -= 8;
225228
}
226229
}
227230

228-
if (bufLen) {
229-
ret += this.toStringHex(buf); // here we have less then 8 bits
231+
// Write out remaining bits if needed
232+
if (bufLen > 0) {
233+
result += Helper.toStringHex(buf & 0xff);
230234
}
231235

232236
if (alignBits) {
233237
length++; // Add 1 to length to account for the padding septet
234238
}
235239

236-
return { length, result: ret };
240+
return { length, result };
237241
}
238242

239243
/**
@@ -248,7 +252,7 @@ export class Helper {
248252

249253
for (let i = 0; i < text.length; i++) {
250254
const byte = Helper.order(text.substring(i, i + 1));
251-
pdu += this.toStringHex(byte, 4);
255+
pdu += Helper.toStringHex(byte, 4);
252256
length += 2;
253257
}
254258

@@ -263,12 +267,6 @@ export class Helper {
263267
* @returns The number as a hexadecimal string
264268
*/
265269
static toStringHex(number: number, fill = 2) {
266-
let str = number.toString(16);
267-
268-
while (str.length < fill) {
269-
str = '0' + str;
270-
}
271-
272-
return str.toUpperCase();
270+
return number.toString(16).padStart(fill, '0').toUpperCase();
273271
}
274272
}

tests/creating.test.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { describe, expect, test } from 'vitest';
2+
import { Submit } from '../src/index';
3+
4+
describe('Test PDU creation', () => {
5+
test('should correctly create a single-part Submit PDU message', () => {
6+
const submit = new Submit('+1234567890', 'Hello, this is a simple Submit.');
7+
8+
const pduParts = submit.getPartStrings();
9+
expect(pduParts).toHaveLength(1);
10+
11+
expect(pduParts[0]).toBe('0001000A91214365870900001FC8329BFD6681E8E8F41C949E83C2A079BA0D679741D3BAB89DA6BB00');
12+
});
13+
14+
test('should correctly create a multi-part Submit PDU message', () => {
15+
const submit = new Submit(
16+
'+1234567890',
17+
'Hello, this is a long text to reproduce the issue that Adam Smid has provided, I am trying to reproduce this with success. I hope you guys have a good day today, make every day count in your live!'
18+
);
19+
20+
const pduParts = submit.getPartStrings();
21+
expect(pduParts).toHaveLength(2);
22+
23+
expect(pduParts[0]).toBe(
24+
'0041000A91214365870900008D06080403B60201C8329BFD6681E8E8F41C949E83C220F6DB7D06D1CB783A88FE06C9CB70F99B5C1F9741747419949ECFEB65101D1DA68382E4701B346DA7C92074780E82CBDFF634B94C668192A0701B4497E7D3EE3388FE06C9CB70F99B5C1F974174747A0EBAA7E968D0BC3E1E97E77317280942BFE16550FE5D07'
25+
);
26+
expect(pduParts[1]).toBe(
27+
'0041000A91214365870900004706080403B60202A0733D3F07A1C3F632280C3ABFDF6410399C07D1DFE4709E056A87D76550D95E96E741E4701E347ED7DD7450DA0DCABFEB72103B6D2F8700'
28+
);
29+
});
30+
});

0 commit comments

Comments
 (0)