This is the longest lab in the course, and the only one where the final test reproduces a production bug rather than checking a function.
Bytes are numbers 0–255 in a plain array, so you can print your output and read it. The wire format per field:
[tag varint][wire type byte][payload]
wire type 0 varint payload, no length prefix
wire type 1 length varint, then that many bytes of UTF-8
What to implement
writeVarint / readVarint — seven bits of data per byte, high bit as a
continuation flag. Get the boundaries right: 127 is one byte, 128 is two.
encode(record, schema) — write present fields in ascending tag order. A
test decodes your output as text and asserts that no field name appears in it;
that is the property the whole format exists for.
decode(bytes, schema) — return { record, unknown }. A field whose tag is
not in your schema must be skipped using its wire type and length, and kept in
unknown rather than thrown away.
update(bytes, schema, changes) — decode, apply changes, re-encode,
preserving unknown fields.