@@ -92,6 +92,100 @@ void main() {
9292 expect (Uint8List .sublistView (buf, 0 , n), cobsDecode (encoded));
9393 }
9494 });
95+
96+ test ('cobsrDecodeInPlace matches cobsrDecode (differential)' , () {
97+ // Assert that the in-place COBS/R decoder agrees byte-for-byte with the
98+ // slice decoder on valid input, and agrees on acceptance vs rejection for
99+ // arbitrary (possibly invalid) input.
100+ void checkAgrees (List <int > encoded) {
101+ Uint8List ? sliceResult;
102+ var sliceThrew = false ;
103+ try {
104+ sliceResult = cobsrDecode (encoded);
105+ } on CobsDecodeException {
106+ sliceThrew = true ;
107+ }
108+
109+ final buf = Uint8List .fromList (encoded);
110+ Uint8List ? inPlaceResult;
111+ var inPlaceThrew = false ;
112+ try {
113+ final n = cobsrDecodeInPlace (buf);
114+ inPlaceResult = Uint8List .sublistView (buf, 0 , n);
115+ } on CobsDecodeException {
116+ inPlaceThrew = true ;
117+ }
118+
119+ expect (inPlaceThrew, sliceThrew,
120+ reason: 'accept/reject must match for $encoded ' );
121+ if (! sliceThrew) {
122+ expect (inPlaceResult, sliceResult, reason: 'decoding $encoded ' );
123+ }
124+ }
125+
126+ // Golden COBS/R encoded vectors (ported from cobsr_test.dart), chosen to
127+ // exercise reduced final blocks, embedded zeros, and maximal (0xFF) codes.
128+ final golden = < List <int >> [
129+ [0x01 ],
130+ [0x02 ],
131+ [0x7F ],
132+ [0xFF ],
133+ [0x02 , 0x01 ],
134+ [0x03 , ...ascii ('a' ), 0x02 ],
135+ [0x03 , ...ascii ('a' )],
136+ [0xFF , ...ascii ('a' )],
137+ [0x06 , 0x05 , 0x04 , 0x03 , 0x02 , 0x01 ],
138+ [0x35 , ...ascii ('1234' )],
139+ [0x06 , ...ascii ('12345' ), 0x05 , 0x04 , 0x03 , 0x02 , 0x01 ],
140+ [0x06 , ...ascii ('12345' ), ...ascii ('9678' )],
141+ [0x01 , 0x06 , ...ascii ('12345' ), ...ascii ('9678' )],
142+ [0x06 , ...ascii ('12345' ), 0x05 , ...ascii ('6789' ), 0x01 ],
143+ [0x01 , 0x01 ],
144+ [0x01 , 0x01 , 0x01 ],
145+ [0xFE , ...range (1 , 254 )],
146+ [0xFF , ...range (1 , 255 )],
147+ [0xFF , ...range (1 , 255 ), 0xFF ],
148+ [0x01 , 0xFF , ...range (1 , 255 ), 0xFF ],
149+ ];
150+ for (final encoded in golden) {
151+ checkAgrees (encoded);
152+ }
153+
154+ // Large seeded-random corpus.
155+ final rng = Random (0xC0B5B12A );
156+ for (var t = 0 ; t < 6000 ; t++ ) {
157+ // Valid encodings from the reference encoder exercise the reduced-block
158+ // path and embedded-zero blocks.
159+ checkAgrees (cobsrEncode (randomPacket (rng, 700 )));
160+
161+ // Raw random bytes are mostly invalid (embedded zeros / bad codes) and
162+ // pin down accept/reject agreement between the two decoders.
163+ final rawLen = rng.nextInt (48 );
164+ checkAgrees (List <int >.generate (rawLen, (_) => rng.nextInt (256 )));
165+
166+ // Non-zero random bytes are always valid COBS/R and heavily exercise
167+ // high length codes and reduced final blocks.
168+ final nzLen = 1 + rng.nextInt (48 );
169+ checkAgrees (List <int >.generate (nzLen, (_) => 1 + rng.nextInt (255 )));
170+ }
171+ });
172+
173+ test ('cobsrDecodeInPlaceWithSentinel matches slice decode (differential)' ,
174+ () {
175+ final rng = Random (0x1234C0B5 );
176+ for (final s in sentinels) {
177+ for (var t = 0 ; t < 4000 ; t++ ) {
178+ final packet = randomPacket (rng, 700 );
179+ final encoded = cobsrEncodeWithSentinel (packet, s);
180+ final expected = cobsrDecodeWithSentinel (encoded, s);
181+
182+ final buf = Uint8List .fromList (encoded);
183+ final n = cobsrDecodeInPlaceWithSentinel (buf, s);
184+ expect (Uint8List .sublistView (buf, 0 , n), expected);
185+ expect (Uint8List .sublistView (buf, 0 , n), packet);
186+ }
187+ }
188+ });
95189 });
96190
97191 group ('framing with a sentinel' , () {
0 commit comments