Custom types
Subclass c.AdvancedType<T> when you need a format that is not a primitive, nested struct, or built-in helper. Implement:
byteSize— fixed width of this slot in bytesread(bytes, offset, endian, label)— decode bytes toTwrite(bytes, offset, value, endian, label)— encodeTinto the buffer
The codec calls these hooks during c.read / c.write; c.sizeof includes byteSize in the struct total.
ts
import { c } from "@craftycodie/cstruct";
/** Three-byte RGB, no padding. */
class Rgb24 extends c.AdvancedType<[number, number, number]> {
readonly byteSize = 3;
read(
bytes: Uint8Array,
offset: number,
_endian: c.Endian,
_label: string
): [number, number, number] {
return [bytes[offset]!, bytes[offset + 1]!, bytes[offset + 2]!];
}
write(
bytes: Uint8Array,
offset: number,
value: [number, number, number],
_endian: c.Endian,
label: string
): void {
if (value.some((channel) => channel < 0 || channel > 255)) {
throw new c.CStructError(`${label}: channel out of range`);
}
bytes[offset] = value[0];
bytes[offset + 1] = value[1];
bytes[offset + 2] = value[2];
}
}
@c.struct()
class Pixel {
@c.field(new Rgb24())
color!: [number, number, number];
}
const pixel = c.read(Pixel, c.write(Pixel, { color: [255, 128, 0] } as Pixel));C equivalent — three packed bytes, no terminator:
c
typedef struct {
uint8_t color[3]; /* or: uint8_t r, g, b; */
} Pixel;Built-in helpers (CBool, CString, CWString, CTime64) are implemented the same way and are available on c if you need instanceof checks. See the overview for built-in field types.