@@ -25,6 +25,13 @@ schemes such as PPP byte stuffing, COBS never doubles a packet's size.
2525- 🔌 ** Stream framing built in** — turn a raw serial byte stream into a stream of
2626 decoded packets with ` CobsFrameDecoder ` ; chunk boundaries don't have to align
2727 with frames.
28+ - 🚩 ** Configurable delimiter (sentinel)** — encode and decode COBS * and* COBS/R
29+ so the output avoids any chosen byte, not just ` 0x00 ` , letting that byte delimit
30+ frames instead.
31+ - 🏷️ ** Sentinel-aware framing** — the framing helpers and the stream decoder can
32+ split on the chosen ` sentinel ` rather than ` 0x00 ` .
33+ - ♻️ ** In-place decode** — decode basic COBS inside the same buffer with no second
34+ allocation; COBS never expands on decode.
2835- 🎯 ** Zero dependencies, all platforms** — pure Dart (` dart:typed_data ` ), works
2936 on mobile, desktop, web, server and CLI. Uses ` Uint8List ` throughout.
3037- 📏 ** Predictable sizing** — ` maxEncodedLength ` / ` encodingOverhead ` for buffer
@@ -109,6 +116,56 @@ outgoingPackets
109116 .listen(serialPort.add);
110117```
111118
119+ ### A custom delimiter byte (sentinel)
120+
121+ Need a delimiter other than ` 0x00 ` — say a byte your protocol reserves for
122+ framing? The ` …WithSentinel ` variants make the encoding avoid an arbitrary
123+ ` sentinel ` byte instead (they XOR the finished encoding with it), so that byte can
124+ delimit frames:
125+
126+ ``` dart
127+ final data = [0x11, 0x22, 0x00, 0x33];
128+
129+ final encoded = cobsEncodeWithSentinel(data, 0xAA); // [0xA9, 0xBB, 0x88, 0xA8, 0x99] — no 0xAA
130+ final decoded = cobsDecodeWithSentinel(encoded, 0xAA); // [0x11, 0x22, 0x00, 0x33]
131+
132+ // COBS/R has the same pair.
133+ cobsrEncodeWithSentinel(data, 0xAA); // [0xA9, 0xBB, 0x88, 0x99]
134+ ```
135+
136+ ` sentinel == 0 ` is byte-for-byte identical to the plain codec, and decoding never
137+ mutates its input.
138+
139+ ### Decoding in place
140+
141+ Basic COBS never expands on decode, so it can be decoded within the same buffer —
142+ no second allocation. ` cobsDecodeInPlace ` overwrites the buffer with the decoded
143+ bytes and returns their length:
144+
145+ ``` dart
146+ final buffer = Uint8List.fromList(cobs.encode([0x11, 0x22, 0x00, 0x33]));
147+ // buffer == [0x03, 0x11, 0x22, 0x02, 0x33]
148+
149+ final n = cobsDecodeInPlace(buffer); // 4
150+ final decoded = Uint8List.sublistView(buffer, 0, n); // [0x11, 0x22, 0x00, 0x33]
151+ ```
152+
153+ ` cobsDecodeInPlaceWithSentinel(buffer, sentinel) ` does the same for a custom
154+ delimiter. (COBS/R can expand on decode, so it has no in-place form.)
155+
156+ ### Framing on a custom sentinel
157+
158+ ` cobsFrame ` / ` cobsUnframe ` and both stream transformers take the same optional
159+ ` sentinel ` , so a whole link can be delimited by a byte other than ` 0x00 ` :
160+
161+ ``` dart
162+ final frame = cobsFrame([0x11, 0x00, 0x22], sentinel: 0xAA); // [0xA8, 0xBB, 0xA8, 0x88, 0xAA]
163+ final packets = cobsUnframe(frame, sentinel: 0xAA); // [[0x11, 0x00, 0x22]]
164+
165+ // The stream decoder frames on the same byte:
166+ serialPort.transform(const CobsFrameDecoder(sentinel: 0xAA));
167+ ```
168+
112169### Composing with other codecs
113170
114171Because they are ` Codec ` s, you can ` fuse ` COBS with anything:
@@ -148,10 +205,13 @@ same. Compare PPP/SLIP escape stuffing, whose worst case **doubles** the packet.
148205| ` cobs ` / ` cobsr ` | Shared ` Codec ` instances (basic COBS and COBS/R). |
149206| ` cobsEncode ` / ` cobsDecode ` | Direct basic-COBS functions. |
150207| ` cobsrEncode ` / ` cobsrDecode ` | Direct COBS/R functions. |
208+ | ` cobsEncodeWithSentinel ` / ` cobsDecodeWithSentinel ` | Basic COBS against an arbitrary delimiter byte. |
209+ | ` cobsrEncodeWithSentinel ` / ` cobsrDecodeWithSentinel ` | COBS/R against an arbitrary delimiter byte. |
210+ | ` cobsDecodeInPlace ` / ` cobsDecodeInPlaceWithSentinel ` | Decode basic COBS within the buffer; returns the length. |
151211| ` CobsCodec ` , ` CobsEncoder ` , ` CobsDecoder ` | ` dart:convert ` classes for basic COBS. |
152212| ` CobsrCodec ` , ` CobsrEncoder ` , ` CobsrDecoder ` | ` dart:convert ` classes for COBS/R. |
153- | ` cobsFrame ` / ` cobsUnframe ` | Add / split the ` 0x00 ` frame delimiter . |
154- | ` CobsFrameEncoder ` / ` CobsFrameDecoder ` | Stream transformers for framed links. |
213+ | ` cobsFrame ` / ` cobsUnframe ` | Add / split the frame delimiter ( ` 0x00 ` or a custom ` sentinel ` ) . |
214+ | ` CobsFrameEncoder ` / ` CobsFrameDecoder ` | Stream transformers for framed links (optional ` sentinel ` ) . |
155215| ` cobsDelimiter ` | The frame delimiter byte (` 0x00 ` ). |
156216| ` encodingOverhead ` / ` maxEncodedLength ` | Size bounds. |
157217| ` cobsMaxBlockLength ` | Max data bytes per COBS block (` 254 ` ). |
0 commit comments