01 · Problem
Wofür braucht man das?
Binärformate sparen Platz und geben ein exaktes Wire-Layout vor, verlangen dafür aber konsistente Feldreihenfolge, Größenberechnung, Bounds-Prüfung und Versionierung.
02 · Denkmodell
Das mentale Modell
Jeder Datentyp ist ein Codec mit drei Operationen: Größe berechnen, ab Offset schreiben und ab Offset lesen. Strukturen komponieren diese kleinen Codecs in expliziter Reihenfolge.
Serialisiert feste Layouts aus vorzeichenlosen Ganzzahlen, Booleans, UTF-8-Strings und Arrays. Dies ist ein festes Layout ohne Versionskennung. Schema-Reihenfolge und Migration müssen beide Seiten vereinbaren; Decorators folgen dem Legacy-TypeScript-Vertrag.
03 · Kontrollfluss
Was passiert in welcher Reihenfolge?
- Schema mit fester Feldreihenfolge und eindeutigem Typ pro Feld definieren.
- Exakte Bytezahl vor der Allokation rekursiv berechnen.
- Felder schreiben und den Offset nach jeder Operation fortführen.
- Beim Lesen Bounds prüfen, Werte rekonstruieren und finalen Offset zurückgeben.
- Formatkennung und Version vor Nutzdaten setzen.
04 · Bauteile
Die entscheidenden Verträge
BinarySerializer.serialize / deserializeLiest und schreibt explizite feste Layoutschemas.Serializable / AsNumber / AsBoolean / AsString / AsArray / SerializedDeklariert Legacy-Decorator-Schemas einschließlich verschachtelter serialisierbarer Felder.05 · Selbst bauen
Selbst implementieren
Ein explizites Codec-Objekt ist leichter zu testen und zu versionieren als implizite Decorator-Metadaten.
- Definiere einen Codec-Vertrag, der Wert und Offset gemeinsam weiterreicht.
- Baue Primitive wie u32 und bool mit strikten Bereichsprüfungen.
- Komponiere Arrays und Structs, ohne Feldreihenfolge aus Reflection abzuleiten.
type Decoded<T> = { value: T; offset: number };
interface Codec<T> {
size(value: T): number;
encode(view: DataView, offset: number, value: T): number;
decode(view: DataView, offset: number): Decoded<T>;
}
const u32: Codec<number> = {
size: () => 4,
encode: (view, at, value) => (view.setUint32(at, value, true), at + 4),
decode: (view, at) => ({ value: view.getUint32(at, true), offset: at + 4 }),
};06 · Verifizieren
Was solltest du testen?
- encode gefolgt von decode ergibt für jedes gültige Objekt denselben Wert und Endoffset.
- Zu kurze Buffer, zu große Zahlen und falsche Längen werden vor dem Zugriff abgelehnt.
- Bekannte Golden Bytes sichern Endianness, Feldreihenfolge und Versionsheader.
Selbst ausprobieren
Formuliere die erste Prüfaussage als fehlschlagenden Test, implementiere die kleinste Korrektur und ergänze einen Grenzfall.
encode gefolgt von decode ergibt für jedes gültige Objekt denselben Wert und Endoffset.
Ansatz vergleichen
Dein Test sollte beim gezielten Verletzen der Invariante fehlschlagen und nach der Korrektur wieder bestehen. Halte den Aufbau deterministisch und prüfe ein beobachtbares Ergebnis.
07 · Grenzen
Kompromisse und Stolperfallen
- Feste Felder sind schnell, aber schlecht erweiterbar; variable Felder benötigen Längenpräfixe.
- UTF-8-Längen zählen Bytes, nicht JavaScript-Zeichen.
- Schemaänderungen ohne Versionsstrategie machen gespeicherte Daten unlesbar.
Dies ist ein festes Layout ohne Versionskennung. Schema-Reihenfolge und Migration müssen beide Seiten vereinbaren; Decorators folgen dem Legacy-TypeScript-Vertrag.
08 · Weiterdenken
Quellcode und verwandte Ideen
yarn tsx packages/binary-serializer/examples/basic.ts