Exercise 2 solution import struct HEADER = struct.Struct(" 0xFFFF: raise ValueError("value too long for a 2-byte length") return HEADER.pack(t, len(value)) + value def iter_tlv(buf): pos = 0 while pos < len(buf): if len(buf) - pos < HEADER.size: raise ValueError(f"truncated header at offset {pos}") t, n = HEADER.unpack_from(buf, pos) start = pos + HEADER.size if start + n > len(buf): raise ValueError(f"record at {pos} wants {n} bytes, {len(buf) - start} left") yield t, buf[start:start + n] pos = start + n def encode(rec): out = pack_tlv(NAME, rec["name"].encode("utf-8")) out += pack_tlv(AGE, struct.pack("", e) # 01 03 00 41 64 61 02 01 00 24 03 04 00 00 00 20 40 # {'unknown': [(200, b'future')], 'name': 'Ada', 'age': 36, 'score': 2.5} # True # {'unknown': [], 'age': 36, 'name': 'Bo', 'score': 1.0} # wrong size -> age must be 1 byte, got 2 # cut value -> record at 10 wants 4 bytes, 2 left # cut header -> truncated header at offset 17 (Run as written; the output matched the comments.) Explanation: Each record is a type byte, a two-byte little-endian length and the value, so a reader that does not know a type can still step over it by its length. The decoder keeps unknown records and the encoder writes them back, which is what lets an older program pass a newer file through without losing data. Known types still have their value size checked, and the iterator checks both the header and the value against the bytes that remain. Order of records does not matter to the decoder.