Skip to content

Commit 2878642

Browse files
authored
Merge pull request #49 from jsonjoy-com/copilot/fix-48
Fix JsonDecoder parsing of large floats with e+ scientific notation
2 parents 1c28fed + da645b0 commit 2878642

File tree

3 files changed

+122
-16
lines changed

3 files changed

+122
-16
lines changed
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/**
2+
* Large floating point numbers and edge cases that should be handled correctly
3+
* by JSON decoders, especially those with scientific notation (e+/e-).
4+
*/
5+
export default {
6+
// Maximum finite representable value in JavaScript
7+
maxValue: 1.7976931348623157e308,
8+
9+
// Same value with different notations
10+
maxValueUppercase: '1.7976931348623157E+308',
11+
maxValueImplicitPlus: '1.7976931348623157e308',
12+
13+
// Values that become Infinity
14+
overflowToInfinity: 2e308,
15+
16+
// Medium range scientific notation
17+
mediumLarge: 1.2345e50,
18+
mediumSmall: 1.2345e-50,
19+
20+
// Very small numbers
21+
verySmall: 5e-324,
22+
smallestNormal: 2.2250738585072014e-308,
23+
24+
// Edge cases in arrays and objects
25+
arrayWithLargeFloats: [1.7976931348623157e308, 2e308, 1.2345e-50],
26+
objectWithLargeFloat: {
27+
maxValue: 1.7976931348623157e308,
28+
infinity: 2e308,
29+
tiny: 5e-324,
30+
},
31+
32+
// Mixed with other types
33+
mixedData: {
34+
numbers: [1, -1, 0, 1.7976931348623157e308, 5e-324],
35+
strings: ['normal', 'with spaces'],
36+
nested: {
37+
largeFloat: 1.2345e100,
38+
boolean: true,
39+
nullValue: null,
40+
},
41+
},
42+
};

src/json/JsonDecoder.ts

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -349,95 +349,95 @@ export class JsonDecoder implements BinaryJsonDecoder {
349349
}
350350
const c9 = c;
351351
c = uint8[x++];
352-
if (!c || ((c < 45 || c > 57) && c !== 69 && c !== 101)) {
352+
if (!c || ((c < 45 || c > 57) && c !== 43 && c !== 69 && c !== 101)) {
353353
reader.x = x - 1;
354354
const num = +fromCharCode(c1, c2, c3, c4, c5, c6, c7, c8, c9);
355355
if (num !== num) throw new Error('Invalid JSON');
356356
return num;
357357
}
358358
const c10 = c;
359359
c = uint8[x++];
360-
if (!c || ((c < 45 || c > 57) && c !== 69 && c !== 101)) {
360+
if (!c || ((c < 45 || c > 57) && c !== 43 && c !== 69 && c !== 101)) {
361361
reader.x = x - 1;
362362
const num = +fromCharCode(c1, c2, c3, c4, c5, c6, c7, c8, c9, c10);
363363
if (num !== num) throw new Error('Invalid JSON');
364364
return num;
365365
}
366366
const c11 = c;
367367
c = uint8[x++];
368-
if (!c || ((c < 45 || c > 57) && c !== 69 && c !== 101)) {
368+
if (!c || ((c < 45 || c > 57) && c !== 43 && c !== 69 && c !== 101)) {
369369
reader.x = x - 1;
370370
const num = +fromCharCode(c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11);
371371
if (num !== num) throw new Error('Invalid JSON');
372372
return num;
373373
}
374374
const c12 = c;
375375
c = uint8[x++];
376-
if (!c || ((c < 45 || c > 57) && c !== 69 && c !== 101)) {
376+
if (!c || ((c < 45 || c > 57) && c !== 43 && c !== 69 && c !== 101)) {
377377
reader.x = x - 1;
378378
const num = +fromCharCode(c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12);
379379
if (num !== num) throw new Error('Invalid JSON');
380380
return num;
381381
}
382382
const c13 = c;
383383
c = uint8[x++];
384-
if (!c || ((c < 45 || c > 57) && c !== 69 && c !== 101)) {
384+
if (!c || ((c < 45 || c > 57) && c !== 43 && c !== 69 && c !== 101)) {
385385
reader.x = x - 1;
386386
const num = +fromCharCode(c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13);
387387
if (num !== num) throw new Error('Invalid JSON');
388388
return num;
389389
}
390390
const c14 = c;
391391
c = uint8[x++];
392-
if (!c || ((c < 45 || c > 57) && c !== 69 && c !== 101)) {
392+
if (!c || ((c < 45 || c > 57) && c !== 43 && c !== 69 && c !== 101)) {
393393
reader.x = x - 1;
394394
const num = +fromCharCode(c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14);
395395
if (num !== num) throw new Error('Invalid JSON');
396396
return num;
397397
}
398398
const c15 = c;
399399
c = uint8[x++];
400-
if (!c || ((c < 45 || c > 57) && c !== 69 && c !== 101)) {
400+
if (!c || ((c < 45 || c > 57) && c !== 43 && c !== 69 && c !== 101)) {
401401
reader.x = x - 1;
402402
const num = +fromCharCode(c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15);
403403
if (num !== num) throw new Error('Invalid JSON');
404404
return num;
405405
}
406406
const c16 = c;
407407
c = uint8[x++];
408-
if (!c || ((c < 45 || c > 57) && c !== 69 && c !== 101)) {
408+
if (!c || ((c < 45 || c > 57) && c !== 43 && c !== 69 && c !== 101)) {
409409
reader.x = x - 1;
410410
const num = +fromCharCode(c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16);
411411
if (num !== num) throw new Error('Invalid JSON');
412412
return num;
413413
}
414414
const c17 = c;
415415
c = uint8[x++];
416-
if (!c || ((c < 45 || c > 57) && c !== 69 && c !== 101)) {
416+
if (!c || ((c < 45 || c > 57) && c !== 43 && c !== 69 && c !== 101)) {
417417
reader.x = x - 1;
418418
const num = +fromCharCode(c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17);
419419
if (num !== num) throw new Error('Invalid JSON');
420420
return num;
421421
}
422422
const c18 = c;
423423
c = uint8[x++];
424-
if (!c || ((c < 45 || c > 57) && c !== 69 && c !== 101)) {
424+
if (!c || ((c < 45 || c > 57) && c !== 43 && c !== 69 && c !== 101)) {
425425
reader.x = x - 1;
426426
const num = +fromCharCode(c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17, c18);
427427
if (num !== num) throw new Error('Invalid JSON');
428428
return num;
429429
}
430430
const c19 = c;
431431
c = uint8[x++];
432-
if (!c || ((c < 45 || c > 57) && c !== 69 && c !== 101)) {
432+
if (!c || ((c < 45 || c > 57) && c !== 43 && c !== 69 && c !== 101)) {
433433
reader.x = x - 1;
434434
const num = +fromCharCode(c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17, c18, c19);
435435
if (num !== num) throw new Error('Invalid JSON');
436436
return num;
437437
}
438438
const c20 = c;
439439
c = uint8[x++];
440-
if (!c || ((c < 45 || c > 57) && c !== 69 && c !== 101)) {
440+
if (!c || ((c < 45 || c > 57) && c !== 43 && c !== 69 && c !== 101)) {
441441
reader.x = x - 1;
442442
const num = +fromCharCode(
443443
c1,
@@ -466,7 +466,7 @@ export class JsonDecoder implements BinaryJsonDecoder {
466466
}
467467
const c21 = c;
468468
c = uint8[x++];
469-
if (!c || ((c < 45 || c > 57) && c !== 69 && c !== 101)) {
469+
if (!c || ((c < 45 || c > 57) && c !== 43 && c !== 69 && c !== 101)) {
470470
reader.x = x - 1;
471471
const num = +fromCharCode(
472472
c1,
@@ -496,7 +496,7 @@ export class JsonDecoder implements BinaryJsonDecoder {
496496
}
497497
const c22 = c;
498498
c = uint8[x++];
499-
if (!c || ((c < 45 || c > 57) && c !== 69 && c !== 101)) {
499+
if (!c || ((c < 45 || c > 57) && c !== 43 && c !== 69 && c !== 101)) {
500500
reader.x = x - 1;
501501
const num = +fromCharCode(
502502
c1,
@@ -527,7 +527,7 @@ export class JsonDecoder implements BinaryJsonDecoder {
527527
}
528528
const c23 = c;
529529
c = uint8[x++];
530-
if (!c || ((c < 45 || c > 57) && c !== 69 && c !== 101)) {
530+
if (!c || ((c < 45 || c > 57) && c !== 43 && c !== 69 && c !== 101)) {
531531
reader.x = x - 1;
532532
const num = +fromCharCode(
533533
c1,
@@ -559,7 +559,7 @@ export class JsonDecoder implements BinaryJsonDecoder {
559559
}
560560
const c24 = c;
561561
c = uint8[x++];
562-
if (!c || ((c < 45 || c > 57) && c !== 69 && c !== 101)) {
562+
if (!c || ((c < 45 || c > 57) && c !== 43 && c !== 69 && c !== 101)) {
563563
reader.x = x - 1;
564564
const num = +fromCharCode(
565565
c1,

src/json/__tests__/JsonDecoder.spec.ts

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,70 @@ describe('number', () => {
160160
const value = decoder.readAny();
161161
expect(value).toBe(smallFloat);
162162
});
163+
164+
test('large float with e+ notation - Number.MAX_VALUE', () => {
165+
const data = Buffer.from('1.7976931348623157e+308', 'utf-8');
166+
decoder.reader.reset(data);
167+
const value = decoder.readAny();
168+
expect(value).toBe(1.7976931348623157e308);
169+
expect(value).toBe(Number.MAX_VALUE);
170+
});
171+
172+
test('large float with E+ notation - uppercase', () => {
173+
const data = Buffer.from('1.7976931348623157E+308', 'utf-8');
174+
decoder.reader.reset(data);
175+
const value = decoder.readAny();
176+
expect(value).toBe(1.7976931348623157e308);
177+
});
178+
179+
test('large float without explicit + sign', () => {
180+
const data = Buffer.from('1.7976931348623157e308', 'utf-8');
181+
decoder.reader.reset(data);
182+
const value = decoder.readAny();
183+
expect(value).toBe(1.7976931348623157e308);
184+
});
185+
186+
test('infinity with e+ notation', () => {
187+
const data = Buffer.from('2e+308', 'utf-8');
188+
decoder.reader.reset(data);
189+
const value = decoder.readAny();
190+
expect(value).toBe(Infinity);
191+
});
192+
193+
test('medium large float with e+ notation', () => {
194+
const data = Buffer.from('1.2345e+50', 'utf-8');
195+
decoder.reader.reset(data);
196+
const value = decoder.readAny();
197+
expect(value).toBe(1.2345e50);
198+
});
199+
200+
test('very small float with e- notation', () => {
201+
const data = Buffer.from('5e-324', 'utf-8');
202+
decoder.reader.reset(data);
203+
const value = decoder.readAny();
204+
expect(value).toBe(5e-324);
205+
});
206+
207+
test('smallest normal positive float', () => {
208+
const data = Buffer.from('2.2250738585072014e-308', 'utf-8');
209+
decoder.reader.reset(data);
210+
const value = decoder.readAny();
211+
expect(value).toBe(2.2250738585072014e-308);
212+
});
213+
214+
test('large float in JSON array', () => {
215+
const data = Buffer.from('[1.7976931348623157e+308]', 'utf-8');
216+
decoder.reader.reset(data);
217+
const value = decoder.readAny();
218+
expect(value).toEqual([1.7976931348623157e308]);
219+
});
220+
221+
test('large float in JSON object', () => {
222+
const data = Buffer.from('{"value": 1.7976931348623157e+308}', 'utf-8');
223+
decoder.reader.reset(data);
224+
const value = decoder.readAny();
225+
expect(value).toEqual({value: 1.7976931348623157e308});
226+
});
163227
});
164228

165229
describe('string', () => {

0 commit comments

Comments
 (0)