Consistent overhead byte stuffing: Difference between revisions

Content added Content deleted
(Add Python)
Line 521: Line 521:
02 03 04 ... FE FF 00 -> FF 02 03 04 ... FE FF 01 01 00 encode:true, decode:true
02 03 04 ... FE FF 00 -> FF 02 03 04 ... FE FF 01 01 00 encode:true, decode:true
03 04 05 ... FF 00 01 -> FE 03 04 05 ... FF 02 01 00 encode:true, decode:true
03 04 05 ... FF 00 01 -> FE 03 04 05 ... FF 02 01 00 encode:true, decode:true
</pre>

=={{header|Python}}==

Based on several existing solutions. Assumes a zero byte packet delimiter.

<syntaxhighlight lang="python">
"""Consistent overhead byte stuffing. Requires Python >= 3.10."""

from itertools import islice


def cob_encode(data: bytes | bytearray) -> bytearray:
buffer = bytearray([0])
code_index = 0
code = 1
final_code = True

for byte in data:
final_code = True
if byte:
buffer.append(byte)
code += 1

if not byte or code == 0xFF:
if code == 0xFF:
final_code = False

buffer[code_index] = code
code = 1
buffer.append(0)
code_index = len(buffer) - 1

if final_code:
assert not buffer[code_index]
buffer[code_index] = code

if buffer[-1]:
buffer.append(0)

return buffer


def cob_decode(encoded: bytes | bytearray) -> bytearray:
buffer = bytearray()
code = 0xFF
block = 0

for byte in islice(encoded, len(encoded) - 1):
if block:
buffer.append(byte)
else:
if code != 0xFF:
buffer.append(0)

block = code = byte

if not code:
break

block -= 1

return buffer


EXAMPLES = [
(bytearray.fromhex("00"), bytearray.fromhex("01 01 00")),
(bytearray.fromhex("00 00"), bytearray.fromhex("01 01 01 00")),
(bytearray.fromhex("00 11 00"), bytearray.fromhex("01 02 11 01 00")),
(bytearray.fromhex("11 22 00 33"), bytearray.fromhex("03 11 22 02 33 00")),
(bytearray.fromhex("11 22 33 44"), bytearray.fromhex("05 11 22 33 44 00")),
(bytearray.fromhex("11 00 00 00"), bytearray.fromhex("02 11 01 01 01 00")),
(
bytearray(range(0x01, 0xFE + 1)),
bytearray([0xFF, *range(1, 0xFE + 1), 0x00]),
),
(
bytearray(range(0x00, 0xFE + 1)),
bytearray([0x01, 0xFF, *range(1, 0xFE + 1), 0x00]),
),
(
bytearray(range(1, 0xFF + 1)),
bytearray([0xFF, *range(1, 0xFE + 1), 0x02, 0xFF, 0x00]),
),
(
bytearray([*range(0x02, 0xFF + 1), 0x00]),
bytearray([0xFF, *range(2, 0xFF + 1), 0x01, 0x01, 0x00]),
),
(
bytearray([*range(0x03, 0xFF + 1), 0x00, 0x01]),
bytearray([0xFE, *range(0x03, 0xFF + 1), 0x02, 0x01, 0x00]),
),
]


def pretty_hex(bytes: bytearray, m: int, n: int) -> str:
if len(bytes) < m:
return bytes.hex(" ").upper()
return f"{bytes[:n].hex(' ').upper()} ... {bytes[-n:].hex(' ').upper()}"


def main():
for data, expect in EXAMPLES:
encoded = cob_encode(data)
assert encoded == expect
assert cob_decode(encoded) == data
print(f"{pretty_hex(data, 5, 3):<23} -> {pretty_hex(encoded, 7, 4):<33}")


if __name__ == "__main__":
main()
</syntaxhighlight>

{{out}}
<pre>
00 -> 01 01 00
00 00 -> 01 01 01 00
00 11 00 -> 01 02 11 01 00
11 22 00 33 -> 03 11 22 02 33 00
11 22 33 44 -> 05 11 22 33 44 00
11 00 00 00 -> 02 11 01 01 01 00
01 02 03 ... FC FD FE -> FF 01 02 03 ... FC FD FE 00
00 01 02 ... FC FD FE -> 01 FF 01 02 ... FC FD FE 00
01 02 03 ... FD FE FF -> FF 01 02 03 ... FE 02 FF 00
02 03 04 ... FE FF 00 -> FF 02 03 04 ... FF 01 01 00
03 04 05 ... FF 00 01 -> FE 03 04 05 ... FF 02 01 00
</pre>
</pre>