@@ -196,3 +196,174 @@ describe("Extended regression coverage - nested and escaped payloads", () => {
196196 '"line\\nbreak"' ,
197197 ) ;
198198} ) ;
199+
200+ // ─── helpers ──────────────────────────────────────────────────────────────────
201+
202+ @json
203+ class Vec2CovGapFloat {
204+ x : f64 = 0 ;
205+ y : f64 = 0 ;
206+ }
207+
208+
209+ @json
210+ class F32Holder {
211+ v : f32 = 0.0 ;
212+ }
213+
214+
215+ @json
216+ class FloatArr {
217+ values : f64 [ ] = [ ] ;
218+ }
219+
220+ // ─── Serialize: @json class with f32 field ────────────────────────────────────
221+
222+ describe ( "Serialize: @json class with f32 field" , ( ) => {
223+ const h = new F32Holder ( ) ;
224+ h . v = 1.5 ;
225+ expect ( JSON . stringify ( h ) ) . toBe ( '{"v":1.5}' ) ;
226+ expect ( JSON . parse < F32Holder > ( '{"v":3.14}' ) . v . toString ( ) ) . toBe (
227+ ( < f32 > 3.14 ) . toString ( ) ,
228+ ) ;
229+ } ) ;
230+
231+ // ─── swar/float.ts: deserializeFloatField_SWAR exponent paths ─────────────────
232+
233+ describe ( "SWAR: f64 struct field with positive exponent covers exponent block" , ( ) => {
234+ const v = JSON . parse < Vec2CovGapFloat > ( '{"x":1.5e2,"y":3.0e0}' ) ;
235+ expect ( v . x ) . toBe ( 150.0 ) ;
236+ expect ( v . y ) . toBe ( 3.0 ) ;
237+ } ) ;
238+
239+ describe ( "SWAR: f64 struct field with plus-sign exponent covers ASCII_PLUS branch" , ( ) => {
240+ const v = JSON . parse < Vec2CovGapFloat > ( '{"x":2.0e+1,"y":5.0e+0}' ) ;
241+ expect ( v . x ) . toBe ( 20.0 ) ;
242+ expect ( v . y ) . toBe ( 5.0 ) ;
243+ } ) ;
244+
245+ describe ( "SWAR: f64 struct field with negative exponent covers ASCII_MINUS branch" , ( ) => {
246+ const v = JSON . parse < Vec2CovGapFloat > ( '{"x":1.5e-2,"y":2.0e-1}' ) ;
247+ expect ( v . x ) . toBeCloseTo ( 0.015 ) ;
248+ expect ( v . y ) . toBeCloseTo ( 0.2 ) ;
249+ } ) ;
250+
251+ describe ( "SWAR: f64 struct field with large exponent covers scientific fallback path" , ( ) => {
252+ const v = JSON . parse < Vec2CovGapFloat > ( '{"x":1e100,"y":2e-100}' ) ;
253+ expect ( isFinite ( v . x ) ) . toBe ( true ) ;
254+ expect ( v . x ) . toBeCloseTo ( 1e100 ) ;
255+ } ) ;
256+
257+ describe ( "SWAR: f64 struct field with >19 mantissa digits covers fallbackField" , ( ) => {
258+ const v = JSON . parse < Vec2CovGapFloat > ( '{"x":1.23456789012345678901,"y":0.0}' ) ;
259+ expect ( v . x ) . toBeCloseTo ( 1.2345678901234568 ) ;
260+ expect ( v . y ) . toBe ( 0.0 ) ;
261+ } ) ;
262+
263+ // ─── swar/float.ts: deserializeFloat_SWAR exponent loop non-digit break ──────
264+
265+ describe ( "SWAR: standalone f64 with trailing space after exponent covers d>9 break" , ( ) => {
266+ expect ( JSON . parse < f64 > ( "1e5 " ) . toString ( ) ) . toBe ( ( 1e5 ) . toString ( ) ) ;
267+ } ) ;
268+
269+ describe ( "SWAR: standalone f64 with 5-digit exponent covers expDigits>4 standalone path" , ( ) => {
270+ expect ( ! isFinite ( JSON . parse < f64 > ( "1e55555" ) ) ) . toBe ( true ) ;
271+ } ) ;
272+
273+ // ─── swar/float.ts: fallbackField f32 path ────────────────────────────────────
274+
275+ describe ( "SWAR: f32 struct field with >19 mantissa digits covers fallbackField f32 branch" , ( ) => {
276+ const h = JSON . parse < F32Holder > ( '{"v":1.23456789012345678901}' ) ;
277+ expect ( h . v ) . toBeCloseTo ( < f32 > 1.2345678901234568 ) ;
278+ } ) ;
279+
280+ // ─── swar/float.ts: deserializeFloatField_SWAR 5-digit exponent path ──────────
281+
282+ describe ( "SWAR: f64 struct field with 5-digit exponent covers expDigits>4 in struct" , ( ) => {
283+ const v = JSON . parse < Vec2CovGapFloat > ( '{"x":1e55555,"y":0.0}' ) ;
284+ expect ( ! isFinite ( v . x ) ) . toBe ( true ) ;
285+ expect ( v . y ) . toBe ( 0.0 ) ;
286+ } ) ;
287+
288+ // ─── f64[] as @json field → deserializeFloatArrayBody ────────────────────────
289+
290+ describe ( "SWAR: f64[] as @json class field round-trips" , ( ) => {
291+ const f = JSON . parse < FloatArr > ( '{"values":[1.5,-2.5,0]}' ) ;
292+ expect ( f . values . length ) . toBe ( 3 ) ;
293+ expect ( f . values [ 0 ] ) . toBe ( 1.5 ) ;
294+ } ) ;
295+
296+ describe ( "SWAR: f64[] empty array as @json class field" , ( ) => {
297+ const f = JSON . parse < FloatArr > ( '{"values":[]}' ) ;
298+ expect ( f . values . length ) . toBe ( 0 ) ;
299+ } ) ;
300+
301+ describe ( "SWAR: f64[] with large exponent triggers scientific() path" , ( ) => {
302+ const f = JSON . parse < FloatArr > ( '{"values":[1e25,5e-25]}' ) ;
303+ expect ( f . values . length ) . toBe ( 2 ) ;
304+ expect ( f . values [ 0 ] ) . toBe ( 1e25 ) ;
305+ } ) ;
306+
307+ describe ( "SWAR: f64[] reparse with fewer elements (resize)" , ( ) => {
308+ const f = JSON . parse < FloatArr > ( '{"values":[1.1,2.2,3.3]}' ) ;
309+ expect ( f . values . length ) . toBe ( 3 ) ;
310+ const f2 = JSON . parse < FloatArr > ( '{"values":[9.9]}' , f ) ;
311+ expect ( f2 . values . length ) . toBe ( 1 ) ;
312+ expect ( f2 . values [ 0 ] ) . toBe ( 9.9 ) ;
313+ } ) ;
314+
315+ // swar/array/float.ts: fallbackStore via >19-digit mantissa
316+ describe ( "SWAR: f64[] with >19 mantissa digits triggers fallbackStore" , ( ) => {
317+ const f = JSON . parse < f64 [ ] > ( "[1.12345678901234567890]" ) ;
318+ expect ( f . length ) . toBe ( 1 ) ;
319+ } ) ;
320+
321+ // swar/array/float.ts: parseFloatElementSWAR exponent paths
322+ describe ( "SWAR: f64[] with e+ notation covers parseFloatElementSWAR positive-exponent path" , ( ) => {
323+ const a = JSON . parse < f64 [ ] > ( "[1e5,2e+3,3e-1]" ) ;
324+ expect ( a . length ) . toBe ( 3 ) ;
325+ expect ( a [ 0 ] ) . toBe ( 100000.0 ) ;
326+ expect ( a [ 1 ] ) . toBe ( 2000.0 ) ;
327+ expect < f64 > ( a [ 2 ] ) . toBeCloseTo ( 0.3 ) ;
328+ } ) ;
329+
330+ describe ( "SWAR: f32[] with >19 mantissa digits covers fallbackStore f32 path" , ( ) => {
331+ const a = JSON . parse < f32 [ ] > ( "[1.12345678901234567890]" ) ;
332+ expect ( a . length ) . toBe ( 1 ) ;
333+ expect < f32 > ( a [ 0 ] ) . toBeCloseTo ( 1.1234568 ) ;
334+ } ) ;
335+
336+ describe ( "SWAR: f64[] with 5-digit exponent covers parseFloatElementSWAR expDigits>4 fallback" , ( ) => {
337+ const a = JSON . parse < f64 [ ] > ( "[1e55555]" ) ;
338+ expect ( a . length ) . toBe ( 1 ) ;
339+ expect ( ! isFinite ( a [ 0 ] ) ) . toBe ( true ) ;
340+ } ) ;
341+
342+ // swar/float.ts + simd/float.ts: expDigits == 0 return expStart
343+ // A struct float field like "1e," has a bare exponent with no following digits.
344+ // In SWAR/SIMD mode the field parser reaches the `if (expDigits == 0)` guard and
345+ // returns expStart (the fallback picks up `1` as the value).
346+ describe ( "SWAR/SIMD: float field with bare exponent (no digits) covers expDigits==0 return path" , ( ) => {
347+ const v = JSON . parse < Vec2CovGapFloat > ( '{"x":1e,"y":0}' ) ;
348+ expect ( v . y ) . toBe ( 0.0 ) ;
349+ } ) ;
350+
351+ // simd/float.ts: 16-digit SIMD mantissa stride loop
352+ // deserializeFloat_SIMD enters the `while (p+30 < srcEnd && intDigits+fracDigits<=3)`
353+ // loop only when the fractional part has ≥16 digits ahead. A 35-digit mantissa
354+ // satisfies both guards and drives the loop body.
355+ describe ( "SIMD: very long float covers 16-digit SIMD mantissa stride loop (lines 79-84)" , ( ) => {
356+ const v = JSON . parse < f64 > ( "1.12345678901234567890123456789012345" ) ;
357+ expect ( v > 1.0 ) . toBe ( true ) ;
358+ expect ( v < 2.0 ) . toBe ( true ) ;
359+ } ) ;
360+
361+ // simd/float.ts:81: parse16Digits boundary break
362+ // For "1.23456789012345e6": intDigits=1, fracDigits=0, so
363+ // (intDigits+fracDigits<=3) is TRUE and (p+30 < srcEnd) is TRUE (32 bytes
364+ // remain). parse16Digits_SIMD reads 16 chars "23456789012345e6"; the 'e' at
365+ // position 14 is not a digit → parsed==U64.MAX_VALUE → line 81 break → fallback.
366+ describe ( "SIMD: 16-digit parse boundary break covers simd/float.ts:81" , ( ) => {
367+ const v = JSON . parse < f64 > ( "1.23456789012345e6" ) ;
368+ expect ( v ) . toBe ( 1234567.89012345 ) ;
369+ } ) ;
0 commit comments