@@ -404,5 +404,219 @@ public void Unmarshall_DeeplyNestedStructure()
404
404
reader . ReadEndMap ( ) ;
405
405
}
406
406
}
407
+
408
+ [ Fact ]
409
+ public void PeekState_DoesNotAssumeEnd_WhenValueIsInNextChunk ( )
410
+ {
411
+ var writer = new CborWriter ( ) ;
412
+ writer . WriteStartMap ( null ) ;
413
+ writer . WriteTextString ( "key" ) ;
414
+ writer . WriteTextString ( new string ( 'X' , 95 ) ) ; // Forces a refill before the value is fully read
415
+ writer . WriteEndMap ( ) ;
416
+
417
+ byte [ ] bytes = writer . Encode ( ) ;
418
+ using var stream = new MemoryStream ( bytes ) ;
419
+ using var reader = new CborStreamReader ( stream ) ;
420
+
421
+ reader . ReadStartMap ( ) ;
422
+ Assert . Equal ( "key" , reader . ReadTextString ( ) ) ;
423
+
424
+ // This should trigger a refill, not treat it as EndMap
425
+ var value = reader . ReadTextString ( ) ;
426
+ Assert . Equal ( 95 , value . Length ) ;
427
+
428
+ reader . ReadEndMap ( ) ;
429
+ }
430
+
431
+ [ Fact ]
432
+ public void Handles_BreakMarker_AtStartOfNextChunk ( )
433
+ {
434
+ var writer = new CborWriter ( ) ;
435
+ writer . WriteStartArray ( null ) ;
436
+ writer . WriteTextString ( new string ( 'A' , 95 ) ) ; // Fills buffer almost completely
437
+ writer . WriteEndArray ( ) ; // Writes 0xFF as break byte
438
+
439
+ var bytes = writer . Encode ( ) ;
440
+ using var stream = new MemoryStream ( bytes ) ;
441
+ using var reader = new CborStreamReader ( stream ) ;
442
+
443
+ reader . ReadStartArray ( ) ;
444
+ string value = reader . ReadTextString ( ) ;
445
+
446
+ // This will cause a refill where the 0xFF is the first byte of the new chunk
447
+ reader . ReadEndArray ( ) ;
448
+
449
+ Assert . Equal ( CborReaderState . Finished , reader . PeekState ( ) ) ;
450
+ }
451
+
452
+ [ Fact ]
453
+ public void Handles_MultipleBreakMarkers_AtEndOfStream ( )
454
+ {
455
+ var writer = new CborWriter ( ) ;
456
+ writer . WriteStartArray ( null ) ;
457
+ writer . WriteStartArray ( null ) ;
458
+ writer . WriteTextString ( "val" ) ;
459
+ writer . WriteEndArray ( ) ; // Ends inner
460
+ writer . WriteEndArray ( ) ; // Ends outer
461
+
462
+ byte [ ] bytes = writer . Encode ( ) ;
463
+ using var stream = new MemoryStream ( bytes ) ;
464
+ using var reader = new CborStreamReader ( stream ) ;
465
+
466
+ reader . ReadStartArray ( ) ; // outer
467
+ reader . ReadStartArray ( ) ; // inner
468
+ Assert . Equal ( "val" , reader . ReadTextString ( ) ) ;
469
+
470
+ // Should skip 0xFF, refill, then skip another 0xFF
471
+ reader . ReadEndArray ( ) ;
472
+ reader . ReadEndArray ( ) ;
473
+
474
+ Assert . Equal ( CborReaderState . Finished , reader . PeekState ( ) ) ;
475
+ }
476
+
477
+ [ Fact ]
478
+ public void DoesNotInferEndMap_WhenContentFollows ( )
479
+ {
480
+ var writer = new CborWriter ( ) ;
481
+ writer . WriteStartMap ( 2 ) ;
482
+ writer . WriteTextString ( "k1" ) ;
483
+ writer . WriteTextString ( new string ( 'A' , 80 ) ) ;
484
+ writer . WriteTextString ( "k2" ) ;
485
+ writer . WriteTextString ( new string ( 'B' , 80 ) ) ;
486
+ writer . WriteEndMap ( ) ;
487
+
488
+ byte [ ] bytes = writer . Encode ( ) ;
489
+ using var stream = new MemoryStream ( bytes ) ;
490
+ using var reader = new CborStreamReader ( stream ) ;
491
+
492
+ reader . ReadStartMap ( ) ;
493
+ Assert . Equal ( "k1" , reader . ReadTextString ( ) ) ;
494
+ Assert . Equal ( 80 , reader . ReadTextString ( ) . Length ) ;
495
+
496
+ Assert . Equal ( "k2" , reader . ReadTextString ( ) ) ;
497
+
498
+ // This should not throw or prematurely infer EndMap
499
+ Assert . Equal ( 80 , reader . ReadTextString ( ) . Length ) ;
500
+
501
+ reader . ReadEndMap ( ) ;
502
+ }
503
+
504
+ [ Fact ]
505
+ public void Handles_DefinedLengthMap_ThatEndsExactlyAtEof ( )
506
+ {
507
+ var writer = new CborWriter ( ) ;
508
+ writer . WriteStartMap ( 1 ) ;
509
+ writer . WriteTextString ( "key" ) ;
510
+ writer . WriteTextString ( "value" ) ;
511
+ writer . WriteEndMap ( ) ; // Definite-length, no break byte
512
+
513
+ var bytes = writer . Encode ( ) ;
514
+ using var stream = new MemoryStream ( bytes ) ;
515
+ using var reader = new CborStreamReader ( stream ) ;
516
+
517
+ reader . ReadStartMap ( ) ;
518
+ Assert . Equal ( "key" , reader . ReadTextString ( ) ) ;
519
+ Assert . Equal ( "value" , reader . ReadTextString ( ) ) ;
520
+ reader . ReadEndMap ( ) ;
521
+
522
+ Assert . Equal ( CborReaderState . Finished , reader . PeekState ( ) ) ;
523
+ }
524
+
525
+ [ Fact ]
526
+ public void Handles_NestedMapEndAtBufferBoundary_FollowedByOuterKey ( )
527
+ {
528
+ var writer = new CborWriter ( ) ;
529
+ writer . WriteStartMap ( null ) ; // Outer map
530
+ writer . WriteTextString ( "outerKey1" ) ;
531
+ writer . WriteStartMap ( null ) ; // Inner map
532
+ writer . WriteTextString ( "innerKey" ) ;
533
+ writer . WriteTextString ( "innerVal" ) ;
534
+ writer . WriteEndMap ( ) ; // Ends inner map
535
+ writer . WriteTextString ( "outerKey2" ) ;
536
+ writer . WriteTextString ( "outerVal" ) ;
537
+ writer . WriteEndMap ( ) ; // Ends outer map
538
+
539
+ byte [ ] bytes = writer . Encode ( ) ;
540
+
541
+ using var stream = new MemoryStream ( bytes ) ;
542
+ using var reader = new CborStreamReader ( stream ) ;
543
+
544
+ reader . ReadStartMap ( ) ;
545
+ Assert . Equal ( "outerKey1" , reader . ReadTextString ( ) ) ;
546
+ reader . ReadStartMap ( ) ;
547
+ Assert . Equal ( "innerKey" , reader . ReadTextString ( ) ) ;
548
+ Assert . Equal ( "innerVal" , reader . ReadTextString ( ) ) ;
549
+
550
+ // This ReadEndMap will bring us to the end of current buffer
551
+ reader . ReadEndMap ( ) ; // This will trigger refill internally if needed
552
+
553
+ // Without a refill, the next ReadTextString() throws:
554
+ // "No more CBOR data items to read in the current context."
555
+ Assert . Equal ( "outerKey2" , reader . ReadTextString ( ) ) ;
556
+ Assert . Equal ( "outerVal" , reader . ReadTextString ( ) ) ;
557
+ reader . ReadEndMap ( ) ;
558
+ Assert . Equal ( CborReaderState . Finished , reader . PeekState ( ) ) ;
559
+ }
560
+
561
+ [ Fact ]
562
+ public void ReadTextString_TriggersRefill_After_ReadEndMap_AtBufferEnd ( )
563
+ {
564
+ var writer = new CborWriter ( ) ;
565
+ writer . WriteStartMap ( null ) ; // Outer map
566
+ writer . WriteTextString ( "outerKey1" ) ;
567
+ writer . WriteStartMap ( null ) ; // Inner map
568
+ writer . WriteTextString ( "innerKey" ) ;
569
+ writer . WriteTextString ( new string ( 'A' , 30 ) ) ; // Ensure we consume most of the buffer
570
+ writer . WriteEndMap ( ) ; // End inner map — should still fit in buffer
571
+ writer . WriteTextString ( "outerKey2" ) ; // Starts in next refill
572
+ writer . WriteTextString ( "outerVal" ) ;
573
+ writer . WriteEndMap ( ) ; // End outer map
574
+
575
+ byte [ ] bytes = writer . Encode ( ) ;
576
+ var stream = new MemoryStream ( bytes ) ;
577
+
578
+ using var reader = new CborStreamReader ( stream ) ;
579
+
580
+ reader . ReadStartMap ( ) ; // outer map
581
+ Assert . Equal ( "outerKey1" , reader . ReadTextString ( ) ) ;
582
+
583
+ reader . ReadStartMap ( ) ; // inner map
584
+ Assert . Equal ( "innerKey" , reader . ReadTextString ( ) ) ;
585
+ Assert . Equal ( new string ( 'A' , 30 ) , reader . ReadTextString ( ) ) ;
586
+
587
+ reader . ReadEndMap ( ) ; // This must succeed without triggering refill
588
+
589
+ // This next read should trigger refill and continue parsing correctly
590
+ Assert . Equal ( "outerKey2" , reader . ReadTextString ( ) ) ;
591
+ Assert . Equal ( "outerVal" , reader . ReadTextString ( ) ) ;
592
+ reader . ReadEndMap ( ) ; // outer map
593
+ Assert . Equal ( CborReaderState . Finished , reader . PeekState ( ) ) ;
594
+ }
595
+
596
+ [ Fact ]
597
+ public void Unmarshall_MultipleNestedIndefiniteMapsEndingAtStreamEnd_ShouldSucceed ( )
598
+ {
599
+ var writer = new CborWriter ( ) ;
600
+ writer . WriteStartMap ( null ) ;
601
+ writer . WriteTextString ( "nested" ) ;
602
+ writer . WriteStartMap ( null ) ;
603
+ writer . WriteTextString ( "deep" ) ;
604
+ writer . WriteTextString ( "value" ) ;
605
+ writer . WriteEndMap ( ) ; // emits 0xFF
606
+ writer . WriteEndMap ( ) ; // emits 0xFF
607
+
608
+ byte [ ] bytes = writer . Encode ( ) ;
609
+ var stream = new MemoryStream ( bytes ) ;
610
+
611
+ using var reader = new CborStreamReader ( stream ) ;
612
+ reader . ReadStartMap ( ) ;
613
+ Assert . Equal ( "nested" , reader . ReadTextString ( ) ) ;
614
+ reader . ReadStartMap ( ) ;
615
+ Assert . Equal ( "deep" , reader . ReadTextString ( ) ) ;
616
+ Assert . Equal ( "value" , reader . ReadTextString ( ) ) ;
617
+ reader . ReadEndMap ( ) ; // should correctly handle 0xFF at end
618
+ reader . ReadEndMap ( ) ; // should correctly handle 0xFF at end
619
+ }
620
+
407
621
}
408
622
0 commit comments