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.
Decorators speichern ein Schema in reflect-metadata und BinarySerializer schreibt Buffer-Felder. Aktuelle Decorator-Konfiguration, Größenberechnung und verschachtelte Objekte sind inkonsistent; der Test kompiliert nicht.
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
@Serializable / @Serialized
Legacy-Decorators für Klassen- und Feldschema.
@AsNumber / @AsBoolean / @AsString / @AsArray
Deklarieren konkrete Binärrepräsentationen.
BinarySerializer.serialize / deserialize
Schreibt oder liest anhand des gespeicherten Schemas.
05 · Build it yourself
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 du testen solltest
- 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.
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.
Wire-Layouts dürfen niemals von Property- oder Decorator-Einfügereihenfolge abhängen. Reihenfolge und Endianness gehören in den expliziten Vertrag.
08 · Weiterdenken