Last active
May 10, 2025 01:52
-
-
Save whitequark/03ce0d9a85f13754cb17a88dd30bb59e to your computer and use it in GitHub Desktop.
Amaranth COBS encoder/decoder
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from amaranth import * | |
from amaranth.lib import data, wiring, memory, stream | |
from amaranth.lib.stream import In, Out | |
__all__ = ["Encoder", "Decoder"] | |
class Encoder(wiring.Component): | |
"""`Consistent Overhead Byte Stuffing <cobs>`_ encoder combined with a FIFO. | |
The encoder accepts a stream of tokens, which can be either _data_ or _end_, and produces | |
a stream of bytes. Input data tokens produce non-NUL output bytes with a maximum latency | |
of 256 cycles; input end tokens produce NUL output bytes. | |
Since COBS encoding requires up to 254 bytes of lookahead, and a COBS encoder will be usually | |
combined with a FIFO (either at the input or at the output), this encoder is combined with | |
a FIFO to use limited memory resources more efficiently. All but one byte of the internal FIFO | |
will be filled with data in case of output back-pressure. | |
The latency of the encoder depends on the type and value of input tokens. Input non-NUL data | |
tokens do not immediately produce output bytes; rather, bytes corresponding to these tokens | |
appear at the output only after: (a) an input end token, or (b) an input NUL data token, or | |
(c) 255th consecutive non-NUL data token. | |
.. _cobs: https://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing | |
""" | |
i: In(stream.Signature(data.StructLayout({ | |
"data": 8, | |
"end": 1 | |
}))) | |
o: Out(stream.Signature(8)) | |
def __init__(self, fifo_depth=256): | |
if not (fifo_depth >= 256 and fifo_depth.bit_count() == 1): | |
raise ValueError("COBS encoder requires a power-of-2 sized FIFO that is " | |
"at least 256 bytes deep") | |
self.fifo_depth = fifo_depth | |
super().__init__() | |
def elaborate(self, platform): | |
m = Module() | |
# This implementation improves resource use efficiency by merging the two memories that | |
# would otherwise be necessary in a typical implementation: the FIFO for buffering packet | |
# data, and the lookahead memory for COBS encoding. Specifically, it reuses the "empty" | |
# space in the FIFO for storing bytes that follow a yet-unknown COBS overhead byte; this is | |
# called "staging". Once the value of the overhead byte becomes known, the FIFO write | |
# pointer is advanced simultaneously with the overhead byte being overwriten; this is | |
# called "committing". | |
m.submodules.data = data = memory.Memory(shape=8, depth=self.fifo_depth, init=[]) | |
w_port = data.write_port() | |
r_port = data.read_port(transparent_for=(w_port,)) | |
w_addr = Signal.like(w_port.addr) | |
r_addr = Signal.like(r_port.addr) | |
empty = (w_addr == r_addr) | |
full = (w_addr == r_addr - 1) | |
def write(at, data): | |
m.d.comb += w_port.addr.eq(at) | |
m.d.comb += w_port.data.eq(data) | |
m.d.comb += w_port.en.eq(1) | |
staged = Signal(8, init=1) | |
def stage(data): | |
write(w_addr + staged, data) | |
m.d.sync += staged.eq(staged + 1) | |
def commit(): | |
write(w_addr, staged) | |
m.d.sync += w_addr.eq(w_addr + staged) | |
m.d.sync += staged.eq(1) | |
with m.FSM(): | |
with m.State("Data"): | |
with m.If(self.i.valid & ~full): | |
with m.If(self.i.p.end): | |
m.d.comb += self.i.ready.eq(1) | |
commit() | |
m.next = "End" | |
with m.Elif(staged == 0xff): | |
commit() | |
with m.Elif(self.i.p.data == 0x00): | |
m.d.comb += self.i.ready.eq(1) | |
commit() | |
with m.Else(): | |
m.d.comb += self.i.ready.eq(1) | |
stage(self.i.p.data) | |
with m.State("End"): | |
write(w_addr, 0x00) | |
m.d.sync += w_addr.eq(w_addr + 1) | |
m.next = "Data" | |
m.d.comb += self.o.valid.eq(~empty) | |
m.d.comb += self.o.payload.eq(r_port.data) | |
with m.If(self.o.valid & self.o.ready): | |
m.d.comb += r_port.addr.eq(r_addr + 1) | |
m.d.sync += r_addr.eq(r_addr + 1) | |
with m.Else(): | |
m.d.comb += r_port.addr.eq(r_addr) | |
return m | |
class Decoder(wiring.Component): | |
"""`Consistent Overhead Byte Stuffing <cobs>`_ decoder. | |
Performs an inversion of the transformation done by :class:`Encoder` with a fixed 0 cycle | |
latency. | |
If invalid COBS data is encountered (namely: if a group header byte or data byte is NUL), | |
the decoder transitions to an error state, signaled by the ``error`` output. This state is | |
final, cleared only by a reset. | |
.. _cobs: https://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing | |
""" | |
i: In(stream.Signature(8)) | |
o: Out(stream.Signature(data.StructLayout({ | |
"data": 8, | |
"end": 1 | |
}))) | |
error: Out(1) | |
def elaborate(self, platform): | |
m = Module() | |
count = Signal(8) | |
offset = Signal(8) | |
with m.FSM(): | |
with m.State("Start"): | |
m.d.comb += self.i.ready.eq(1) | |
with m.If(self.i.valid & self.i.ready): | |
m.d.sync += count.eq(1) | |
with m.If(self.i.payload != 0x00): | |
m.d.sync += offset.eq(self.i.payload) | |
m.next = "Data" | |
with m.Else(): | |
m.next = "Error" | |
with m.State("Data"): | |
m.d.comb += self.i.ready.eq(self.o.ready) | |
with m.If(self.i.valid & self.i.ready): | |
with m.If(offset == count): | |
m.d.sync += count.eq(1) | |
with m.If(self.i.payload == 0x00): | |
m.d.comb += self.o.payload.end.eq(1) | |
m.d.comb += self.o.valid.eq(1) | |
m.next = "Start" | |
with m.Else(): | |
m.d.comb += self.o.payload.data.eq(0x00) | |
m.d.comb += self.o.valid.eq(offset != 0xff) | |
m.d.sync += offset.eq(self.i.payload) | |
with m.Else(): | |
m.d.sync += count.eq(count + 1) | |
with m.If(self.i.payload != 0x00): | |
m.d.comb += self.o.payload.data.eq(self.i.payload) | |
m.d.comb += self.o.valid.eq(1) | |
with m.Else(): | |
m.next = "Error" | |
with m.State("Error"): | |
m.d.comb += self.error.eq(1) | |
return m |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from amaranth import * | |
from amaranth.sim import Simulator | |
from amaranth_cobs import * | |
from cobs import cobs | |
def ref_encode(data_i: bytes) -> bytes: | |
return b"\0".join(cobs.encode(chunk) if chunk else b"" for chunk in data_i.split(b"$")) | |
def rtl_encode(data_i: bytes, dollar=True) -> bytes: | |
dut = Encoder() | |
async def testbench_i(ctx): | |
ctx.set(dut.i.valid, 1) | |
for byte in data_i: | |
if byte == ord("$") and dollar: | |
ctx.set(dut.i.payload, {"end": 1}) | |
else: | |
ctx.set(dut.i.payload, {"data": byte}) | |
await ctx.tick().until(dut.i.ready) | |
if not dollar: | |
ctx.set(dut.i.payload, {"end": 1}) | |
await ctx.tick().until(dut.i.ready) | |
ctx.set(dut.i.valid, 0) | |
data_o = bytearray() | |
async def testbench_o(ctx): | |
ctx.set(dut.o.ready, 1) | |
empty_for = 0 | |
while empty_for < len(data_i) + 3: | |
_, _, valid, payload = await ctx.tick().sample(dut.o.valid).sample(dut.o.payload) | |
if valid: | |
data_o.append(payload) | |
empty_for = 0 | |
else: | |
empty_for += 1 | |
ctx.set(dut.o.ready, 0) | |
sim = Simulator(dut) | |
sim.add_clock(1e-6) | |
sim.add_testbench(testbench_i) | |
sim.add_testbench(testbench_o) | |
sim.run() | |
return data_o | |
def rtl_decode(data_i: bytes, dollar=True) -> bytes: | |
dut = Decoder() | |
async def testbench_i(ctx): | |
ctx.set(dut.i.valid, 1) | |
for byte in data_i: | |
ctx.set(dut.i.payload, byte) | |
await ctx.tick().until(dut.i.ready) | |
ctx.set(dut.i.valid, 0) | |
data_o = [] | |
async def testbench_o(ctx): | |
pending = bytearray() | |
ctx.set(dut.o.ready, 1) | |
for _ in range(data_i.count(b"\0")): | |
while True: | |
_, _, valid, payload = await ctx.tick().sample(dut.o.valid).sample(dut.o.payload) | |
if valid: | |
if payload.end: | |
data_o.append(bytes(pending)) | |
pending.clear() | |
break | |
else: | |
pending.append(payload.data) | |
ctx.set(dut.o.ready, 0) | |
sim = Simulator(dut) | |
sim.add_clock(1e-6) | |
sim.add_testbench(testbench_i) | |
sim.add_testbench(testbench_o) | |
sim.run() | |
return data_o | |
def cases() -> list[bytes]: | |
return [ | |
b"\0$", | |
b"\0\0$", | |
b"\0A\0$", | |
b"AB\0C$", | |
b"ABCD$", | |
b"A\0\0\0$", | |
b"A"*254+b"$", | |
b"\0"+b"A"*254+b"$", | |
b"A"*500+b"$", | |
b"foo$bar$", | |
] | |
def test_encoder_simple(): | |
for case in cases(): | |
assert rtl_encode(case) == ref_encode(case) | |
def test_encoder_vmlinuz(): | |
assert rtl_encode(vmlinuz, dollar=False) == cobs.encode(vmlinuz) + b"\0" | |
def test_decoder_simple(): | |
for case in cases(): | |
print(ref_encode(case)) | |
assert rtl_decode(ref_encode(case)) == case.split(b"$")[:-1] | |
def test_decoder_vmlinuz(): | |
assert rtl_decode(cobs.encode(vmlinuz) + b"\0") == [vmlinuz] | |
vmlinuz = bytes.fromhex(""" | |
4d5a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 | |
000000000000cd238281400000005045000064860400000000000000000001000000a00006020b02021400d0b70000700600 | |
00000000fd5cb7000050000000000000000000000010000000020000000000000300000000000000000000000090be000010 | |
00001a42b8000a00000100000000000000000000000000000000000000000000000000000000000000000000000006000000 | |
00000000000000000000000000000000000000000000000000000000000000000032b800c005000000000000000000002e73 | |
65747570000000300000001000000030000000100000000000000000000000000000400000422e636f6d7061740000100000 | |
004000000010000000400000000000000000000000000000400000422e7465787400000000d0b7000050000000d0b7000050 | |
0000000000000000000000000000200000602e64617461000000007006000020b800001200000020b8000000000000000000 | |
00000000400000c0ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff | |
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff270100 | |
207e0b000000ffff000055aaeb6a486472530f0200000000001000430001008000001000000000000000000000000000005c | |
000000000000ffffff7f0000200001157f00ff070000000000000000000000000000cc0200001e6ab4000000000000000000 | |
000000010000000000d07e0360a9b6003cb1b7008cd88ec0fc8cd239c289e27416ba005af60611028074048b16240281c200 | |
04730231d283e2fc7503bafcff8ed0660fb7e2fb1e68a302cb66813e784655aa5a5a7517bf8046b9035a6631c029f9c1e902 | |
f366ab66e84512000066b8eb03000066e8fe000000f4ebfd3806ff027405a2ff02eb00669c0fa00fa8666083ec2c89d689e7 | |
b90b00f366a566610fa90fa1071f669dcd00669c1e060fa00fa86660fc660fb7e48cc88ed88ec0678b7c244421ff740889e6 | |
b90b00f366a583c42c66610fa90fa1669d66c3665666536683ec2c6689c36683f80a750c66b80d00000066e8e3ffffff6689 | |
e066e8d01c000067c7442410070067c7442418010067c644241d0e67885c241c6631c96689e266b81000000066e850ffffff | |
66833ecc5700743966beffff000066a1cc576683c005660fb7c066ff16a047a8207416660fb716cc57660fb6c36683c42c66 | |
5b665eff26a447664e74e6f390ebcd6683c42c665b665e66c366536689c367660fbe0384c0740a664366e84effffffebed66 | |
5b66c3074e6f207365747570207369676e617475726520666f756e642e2e2e0a0066ba800000006631c0ff26a44766566653 | |
66bba086010066be2000000066e8ddffffff66b86400000066ff16a0473cff7506664e7506eb1fa801741366e8beffffff66 | |
b86000000066ff16a047eb04a802740a664b75c66683c8ffeb036631c0665b665e66c366556657665666536689c66631c08e | |
e06683c8ff8ee866bf000200006467668b2f6689e86683ee01722567668d5801646766891f66e860ffffff66b81002000065 | |
67668b006639c374da6631d8eb036631c066ba00020000646766892a665b665e665f665d66c3665666536683ec2c66bbff00 | |
000066b82000000066e87fffffff6685c00f85f5006689e066e8291b000067c744241c01246631c96689e266b81500000066 | |
e8bbfdffff66b82000000066e84affffff6685c00f85c00066e8e4feffff6689c666b82000000066e82effffff6685c00f85 | |
e8f3caffff66c706ec5701000000678a5424486683e27f6631c038d37429660fb606b64738d075066683c8ffeb1967894424 | |
1c6631c96689e266b81000000066e8b3caffffebe16683c458665b665e66c367660fb600e966ff665566576656665366a1f8 | |
5766486631ff6683f8010f87f8008a1eb6476631c08ee066e89cf8ffff6689c566a1044666406683e0fe66a3044666a3e845 | |
66be1401000066a10046662b0604466683f8070f8eb3006689f066e8fbf3ffff6685c00f85960067668d8600ffffff66e8f4 | |
feffff6685c00f85810066ba1000000066b8c003000066e8b7feffffa801756b66ba0600000066b8ce03000066e8a1feffff | |
a8017555660fb7c566ba0f00000066e88dfeffff84c0754166a1044666406683e0fe67668d5008668916044667893067c740 | |
06000066ba4a04000064678b126789500266ba8404000064678a12660fb6d2664267895004664766466681fe800100000f85 | |
3cff660fb6c366e855feffff6689f8665b665e665f665d66c38ed98ec18ee18ee98ed101dc0f00df31c931d231db31ed31ff | |
0f00d1ffe0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 | |
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 | |
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 | |
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 | |
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 | |
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 | |
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 | |
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 | |
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 | |
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 | |
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 | |
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 | |
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 | |
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 | |
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 | |
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 | |
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 | |
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 | |
65722027666f7263657061652720746f20656e61626c6520617420796f7572206f776e207269736b210a006561726c797072 | |
696e746b0073657269616c003078007474795300636f6e736f6c650075617274383235302c696f2c00756172742c696f2c00 | |
65646400736b69706d627200736b6970006f6666006f6e0071756965740050726f62696e672045444420286564643d6f6666 | |
20746f2064697361626c65292e2e2e20006f6b0a006561726c7920636f6e736f6c6520696e20736574757020636f64650a00 | |
6465627567005741524e494e473a20416e6369656e7420626f6f746c6f616465722c20736f6d652066756e6374696f6e616c | |
697479206d6179206265206c696d69746564210a00556e61626c6520746f20626f6f74202d20706c65617365207573652061 | |
206b65726e656c20617070726f70726961746520666f7220796f7572204350552e0a004132302067617465206e6f74207265 | |
73706f6e64696e672c20756e61626c6520746f20626f6f742e2e2e0a005072657373203c454e5445523e20746f2073656520 | |
766964656f206d6f64657320617661696c61626c652c203c53504143453e20746f20636f6e74696e75652c206f7220776169 | |
74203330207365630a004d6f64653a205265736f6c7574696f6e3a2020547970653a20002564782564002563202530335820 | |
25346478252d377320252d367300456e746572206120766964656f206d6f6465206f7220227363616e2220746f207363616e | |
20666f72206164646974696f6e616c206d6f6465733a200008200800556e646566696e656420766964656f206d6f6465206e | |
756d6265723a2025780a004347412f4d44412f484743004547410056474100564553410042494f5300000000000000000000 | |
00000000000000000000000000006670750000056d737200000670616500000863783800000f636d6f760000186678737200 | |
001973736500001a7373653200011d6c6d0003146e6f706c00151f0000000000000000000000000000000000000000000000 | |
0000000000000000618100070000002000000000000000000000000000000000000000000000000000000000000000000000 | |
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f8030000 | |
f802000000000000000000000000000000000000ffff0000009bcf00ffff00000093cf006700001000890000000000000000 | |
0000491d0000111f00001f1f00001f1f00001f1f00001f1f0000111f00001f1f00001f1f00001f1f00001f1f0000df1e0000 | |
3b1f0000961e00001f1f00001f1f0000d31d00001f1f0000171f00001f1f00001f1f0000031f000030313233343536373839 | |
4142434445460000000000000000362e31322e32352d616d643634202864656269616e2d6b65726e656c406c697374732e64 | |
656269616e2e6f72672920233120534d5020505245454d50545f44594e414d49432044656269616e20362e31322e32352d31 | |
2028323032352d30342d32352900eb320000aa320000f33200002f330000fb32000003330000173300000100000002000000 | |
070000006d430000794300007d4300002046000028460000404600007d430000143200003f2f000000000000000000000000 | |
000000000000814300003e3300006035000000000000000000000000000000020002864300002d3800003538000000000000 | |
0000000001000000000180000000000000000000005a0000005a000000000000000000000000000000000000000000000000 | |
0000000f500019000000000f500019000000010f50002b0000000000000000000000000f500019000000010f500032000000 | |
020f50002b000000030f50001c000000050f50001e000000060f500022000000070f50003c00000055aa5a5a000000000000 | |
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 | |
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 | |
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 | |
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 | |
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 | |
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 | |
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 | |
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 | |
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 | |
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 | |
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 | |
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 | |
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 | |
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 | |
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 | |
c2ffa6d3a2ff779c162b389071c9768dbe2ab48afc7f2d34f7af7e988a83885b99380aa91f8450ba9476a5bdca621b81edfc | |
5786b9b1d842b457c541a5a442acc21f757eaa554b68312ca7ab0adc5d9589d9387072f08683ff0f8205f5567997e358c551 | |
e4d68cffffc980d8f3f6bad5ffbfecc38a7bf0dda78581eb318c29058754ac6306088dedc91134f66a67800062c037a68154 | |
8a0641c90f0769d62cb4960675352359c5bdf4a0b0bb2c5af86f4f905f8948771ff6c1048f1b02b470a0e32276e524b4c895 | |
82ffbf2e8364ae2514c9f9f2058a623e03d9d60cc25dbf14d4cec31819d06ce15f4729612ee24b5b7900a78df9af875409b3 | |
fff5ff6b0ef6f27f3da319fca066d72798ffbf5d9eaa45ae042ffccfff7f085b874f6232adaeada6e011c57c2da9fcd74598 | |
dbf8ff754b39c8b46690c34eed366d056f04752423fcffaf54b18d52283462ae5481ffe9cdcf7f60f0b9544b52f6b62e89d3 | |
e579f93f709dd2db677757aeb8d5565e6a7eb1db460c42f2da38e85e2dbf41708d29fb7f4f854efc2809f37f75abb99f8e50 | |
ec4359feab3f987b4f9307b3603e49a195ebe50ff32a46e6146a1453724ff35f338181ffd52bcc3d6d560d8fbe2be3ffab63 | |
bfa4d65642f09adeb5397a44fac9b919e26cfd8691163f33946f5e912a2ca031439f365f7599011cfce4e85670e8103fa16a | |
7ee10782ffda1622f30fa9d2bd26b4d510b51f5c96e793cf944a51d7aef336e2bf105b581b91873922714561afc62528eb99 | |
8731fa51c4bcce26a4b0b6354899e6ffff288f4f031125071760a97a2399e5a428e651460bafe8f651d3ea70dced59b1f5d1 | |
941d98e08693fffeee3d4577ddf3e43b74dea743adc3a2de06abe551245991cf00c64e617ef09b3d403ccf560f169450325a | |
64ddd6b074fa51ee6a9ded49c9faf0447fba437cd2ce1d783d20a17c483bb99111b00d7367a9d3a4c461468aa125d49a2b72 | |
3d5d8b882fef462bb6cea496cfddcdba738479b0f536eb6e0ae62137c7dfb0a0f0b0dc36225d7f4ad33f2d040253b6ce7be1 | |
23cc4f68ee1fdedb6ab3364efb8606b40e67744039369b75c3b1c8d5b00260ee69de9ba5e7d879e8a37ef97006fcaffebbe1 | |
14b99a24fe54c36535dc277df2372e591277c1dc06c8694466ef9653746124308787e8c0478eaa93e46a88bf2ac1fc0103b3 | |
22910ec21511d84d40ee99bddcb8030665aa1a9061de82abacd8ea22b17ba74a3bdb1e0a80392cc7210a8f41cedda5213276 | |
f7109fc2c0d8a3afbd031cf9eb955c4bb2a2f8921204730f1deabf17c80849e51766c338ebedfb64a821df7da101bbc1641a | |
d38b541d6eb610c83b4d2855eb04a80bb82d5bfa2880b90efa9d69a71a135c74370773a731d51dc857995ede054108e10e45 | |
2d9ca0c0d25b5667951fff7f856a870a75e69f9d442f23bf773031773dd72d3aba4271feb4601773bfae3315a3493922396d | |
f2374db00d6e6618c066fe28b706e65ff2552d2e3a3429853e812757e65729eedf0b9c7ff67f8358d533ca7f0db343ee2280 | |
6c37e705dd6e24af15b3dbba88f96e9dea740bc03b3ae8528c60e03c25c3869841ec07213dca5d524caac045cd7db4d08a41 | |
30fb5906f45b305a882b48fe60b399e9ecc7da2fce453dea062b42383b77f5f2ba2bde43062450b6800ec7112d31e5744f2d | |
58abee60972dde85521e3c68611ff86e5992a93d29343a3ded1f2ac4e63bd1d9e087bcd43e8946d63d93ad468e3dca71c80e | |
b846fb79ef21d459218f79c0b33cdd0dd287127d299663461b6da98527bc602c90b579903e962c2dc57eba72b400c7bd5e6a | |
6316b06b8076533cf5d457ce6cfd6e45b81aea14b4704b25b21b197ae7afa3daa1c2ecc8c7bbdd4d9581f539ad5ba4a031ba | |
6b85c671c8badacbc274fe41bc6d0c90691ec57d7fd2df3d2d21c6e9595ef86002df58103e3d934b17a0932afde9650bfa34 | |
47c7ec48b40780c7844eb54622091d81fd97bd6f5ed5eab22a25706892ec1a12d9d17df4338f337ef80084308a38776a0f6c | |
c25205bdb84b437d0521867428e8f3112f01a54b9bb0bb4ee8dd06bd863ac35dece1a0f50fc7e6798da5aa3fabd7be0006f6 | |
6fe8e18ed6f68e6c6529375d6ee0b37c62a4230970800651bcd2a6a1c1ab69c8026696670472f802699f30858f903ee4c044 | |
68b346df0b227c29f75105e94d58024d573a8ad7eb8b3066cb3c0315ee871370f965dc67c9ab615fcc69aace42bc57aa2f50 | |
f8ec46a82dca4c0198a37b403208f4e9921e0ffe83d1e5bc5640f8db02250f1841c0acb9436615f0daf5fedc50bdf6c374e6 | |
f3e9bb1f56c0b865ad329dc1742d18a1c41ae6d0983b021654913262015eda33024ea65ad224c68d941843a33f5404e891d3 | |
024c5e554e432688b034cfe0a06530e64c2f6fe254a725f2cc308a647de42bbc4e3305fc95d3bb8b57fbc45a044af2394bb9 | |
02b2b2b4410dc307ba58fcbdc36c07e46ff4fb414eadbcdaf7482d6f4bb9d7f74bb3d223d11c6940a702da98f2b84b9721b4 | |
04d5c5fd50487ae9c14d00cad2d55d54fa589f01f4642846a1dfa0da4537c5af9c255b31193233d89863ea5419b243fba439 | |
a3427fd4ad7409b439aac841744e057d409aff7236dcf0ba0c97d69de66928f01e5947c68c6907f28136a4cf2d0a721eea3f | |
dc246692518cb27fe1e8c0b3082f981ba5fc88cf77e268668b1914561ac108c30e60309dbf3ee9f53cb38c98bca17c8eeea9 | |
6aee71bdd6284aef16970e5ba2bd93ada3b0347da37c58b467c63b3a242c3d2346af81f846377fc433622b3cfa8d8660f87e | |
23b536f7aeb9b5a9f55ac3d9bac42ff2a4048bea808c476e9233edc243354145263b8bd70f657438bb1f2725ea2a22a459e6 | |
39a874a89a96862c83743b1fb811bcc445d2ec795ec7d1224638b214d779b45f6ea156c1961bf219e4169027c62b409fb672 | |
24f2206e5576c8bf9a5f7bd0e84939dea4d29b3f9c28b9c5d6bb2d55213bda0dbcbdc56768eefcedc6fd18973b10ce651c0d | |
9e04f6179c772986e6f67a403b0bda599ab7d440c1a0a67307a48dc9c034481d3cda3bbf23ad1b2020dff97c4ae829340bac | |
849e66f988d263cad609945c46f0ab20a5473a1717e54aac20805c5a64f2af5512a898d9cab82522bc5e8fa475d764cbfc6f | |
ef6e5ee355ba422b14f5e71857e65ec9c3aa92ad4149ee0545162255ca15a138d11a7009e86caeb4b649b32350795af7073f | |
c1eb7abff4b1d4c4bb277c75c934e8482c38312394558f081926afcef9d869567de513439945798b71e2097d76cad3ebd26b | |
4e85f02c8f88f6b5a63a90deaa49f272d94e6f7c04e1ad903145684003097e5212f874ed283b1acd9439e396c16739324670 | |
63da25508886f61aec9c44bc6b857b9834548da0406b9ccb4a40a10082bee7cb2e3c7731efcb4e208d39df8a2c73c25acf1d | |
9f0f9672f8838c8db45e920d4aeb3f4cc9307e18ada37cb895e9ca9828819fe72341bb0d6137218416b229784b73523398f2 | |
0f5a526bd49a8d065351e004ed7cde529b74d0ad998dd2537e9d80612adb070b9e0929310bace82cf883e6bdb88a8e2a42fc | |
98018529bbcd091b29428525482f45ad7208c98c570de1d7b3016485305a5138ab0a7f4bf23864114ce7c4255a46b4c43926 | |
be1e293f6e8047cf1198274c6ebdd36385109d87f746eba8466381af381054225ff349653c2b1e36bc442d595a37d940c5f4 | |
253487b7feb3a5c8098712bd4085b9c7385cfa60017d117e6d36772186b8825a1985652949ab2d8b00bbc2921c994c1d3a5a | |
2747135eaa832185d2aaaf21434a377faec1c03ccfaf0dc8889270a43fe2b552d1f0728df5c64d37ea7a0fcb99e98c7606b4 | |
f76083555e9dea31a319f4c4d8ea33cee7ac1d85be4657c09667e9a1bbd8a0438c0eb679e86dccbc6f8342463ca2d0356ff1 | |
63073607ad201a5f5766693d089ca3bd9bf75db1097b2f8c239d5bf2ea5ec1ff0ed15d30eadbc47521fbb7d8979e69e16d87 | |
a296407e2bfe74a2bdd46c04f380d2dff244e60527453bd80b3e0abeaa162db0723e5981b768312247d97b2075038f63ae15 | |
96c61cda98ce6db8b855813179ecf2c15cf09646cc075bf61181ba5806e91b366aae3abc6d75b47320a943b8675ae57a8baf | |
b81863085d4834c4149075211454bc7165d2dbc076833bdac2e54cfaf9235ec949e50730e4c8fad52842e67dd8d191c62adc | |
718670b4b3a020603990e8e7a20711bc2aeae84f5a0c6d79860879dae8b49f787bbddb4082d643abc021d57f7c644f06028c | |
3d3844e475c624287b45de5514b2a0b38bbd276e83e253c6afd45a0393151b7d7e50bbb0916e4d27760aa6229385c6363dd6 | |
3bb9c4b5a22fc6af4d6b9a3404595a0929f3107e071f5c4d292767af5aec9cd140a70c9df78a762e3758da49db0c84f972e5 | |
749faaf20272f4a72a31432d477c13e7d742c57b36f153c0f55c5e12d0dd72ae6d5e05e35a63440221afdc52157768f3ad4f | |
be126f4bf55b747841c8f956a3a8226cd93a89eb8ad3f0cbb0f643c5ddf8a39fad03fb2ff9b7ac91dc86f64c978fd982e296 | |
7c0bbb8b714cb2581304270892806e7573060ede0ae87883f70665959ff5c052e433e831905e820a16a0093b05253b98d19e | |
2c31e434e43af29216eccdd3c9a6c0e12fcd4e03036c1d3aa3096a092a5616996c2685e6ac8d1ec4d10f9199a337503141a5 | |
cd2025a8055861aae749ed885a3b0f15b4e513dedae9c33af5855337356e7e2ba09340a726edba58134cf361b526750853ee | |
c6864eefb6f964619d1a8d08b69bdc71ef105d58376c0c93deb25ee037c48f70ac7269bb9b898edde47b6bdcc2a50fd51af3 | |
ec595aa7d3839d7d1a4766f1c6a130d448d42765d7d711a971f033b2eaad97b5821081be77fd68d808566492a9f07e8917e6 | |
795df5879c4de9b4b4c9a4eee900d87e791ee59dfc64a0472679f1e4516a05a0dd8b0a4075e3957ae52cf9758e367391f2bd | |
d25331338e62484e4dbb3414f23bfe678e54839bc218e83aef7eb4b58920abd52eb4264aa830c2f54859103a04bd01686010 | |
bf602d7332231d504c81fa01d4da3eab884aafa86f6efb675b78e7b5ff4902bfea46fb3bb42ecde0cc9845cda95dec2a42f7 | |
85f168c1443aaf017258381c35023b382ff7ee99a16f68722d6db5729116e40473d7ff7de89d4f18c315050e871a8db74e6d | |
f93efb46af589f1909d5507f9b09f9d9826f33a3210ee0c6916fb64ae072044db81c4dab8cfbca045e2d0c974e7a71dadf33 | |
b7f0e85543762f06d377a504e345944e6eaa11aade6cc7557d0215ab5605aaaae6eaacba24d87dfe237a60c36be320409ee7 | |
7073754d7559b28b7708d2d33e9a3d5af90c3fb65214b7cdd1ce8d5d5c1830ec72c8de8bad7cb07c369c6e7d955b9ff6f979 | |
7653cb539c0af5bdad7e4c3c42bfc2ad759a6adf3cdb52cb47759aaeb5146b813cd884f5c93aab3fd779ef9bb6fd875b1771 | |
ebf999766361a8a387ecc910518b38c6f0b82ba807285d6852b5073af314b4b6556b220f361ec3ef454469f52e9d4d236480 | |
7347c1928ba601d5b23467f0b3062eebba6222e42584299770153db295cba5f900b3ebca0608da2eadcdc0d59cff5ed5b972 | |
acbbc2359a7ce9ea445aef3ec1ce423a730315add23cbb9d655ae1cd3c047fc40f80c2f82fdf5e1935415ff307c2daa6bee2 | |
ec514dab0802393e47abf539edd458a9685496346ac092bc60e62c13c0eb0966d0474378f4609e570ebd2ccd7ed4447ad987 | |
5e524604b265dcd8c1966e33ad6572a77ba2ed7d152556dd0e56402b80499bf9038a0f7ce606e1240699681205b9ef89b1a3 | |
f88820467c0094d5e6278430cdee0b7e6629b73269970fc85fbc768b64f06b1f3fa7c29be6935173bed75c7f6a01a655a189 | |
2dfcea90d77b4b492735d4a63291243fdbd0a10c6e29f535d0ac7424c458e506b4d21cc0 | |
""".replace("\n", "")) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment