Skip to content

Unions ​

A union places every member at offset 0, so the members overlap and the union's size equals the widest member. Unions are used for type punning and tagged variants.

Basics ​

ts
import { Union } from '@remotex-labs/xstruct';

const value = new Union<{ int: number; float: number }>({
    int: 'u32le',
    float: 'f32le'
});

value.size; // 4, the widest member

Union extends Struct, so it shares the size, pointerSize, toBuffer, and toObject API and can be nested inside any struct schema.

Write semantics ​

Only the members you provide are written, in declaration order, into the same overlapping bytes. Provide exactly one member per toBuffer call so the result is unambiguous; the rest of the buffer is zero-filled.

ts
const buffer = value.toBuffer({ float: 5.0 }); 

Read semantics ​

Every member is decoded from the same bytes, which is the type punning a union provides.

ts
const decoded = value.toObject(buffer);
decoded.float; // 5.0
decoded.int;   // 1084227584, the same bytes read as a u32le

Heap members ​

A pointer is a fixed-size slot, so it overlaps cleanly with the other members. Unions therefore accept pointer (heap-backed) members such as '*utf8'. The pointer occupies the overlapping stack bytes, and its variable-length payload lives on the heap, outside the union's fixed size.

ts
const cell = new Union<{ word?: number; text?: string }>({
    word: 'u32le',
    text: '*utf8'   // a heap pointer member
});

cell.size; // 4: one u32le or one pointer, whichever is wider

const buf = cell.toBuffer({ text: 'a variable-length heap string' });
cell.toObject(buf).text; // 'a variable-length heap string'

Read back the member you wrote. Reading a different member reinterprets the same overlapping bytes - meaningful for fixed numeric punning, but not across members of different kinds.

TIP

This is new in v3. Earlier versions rejected variable-length strings in a union because they grew the buffer inline. Now that variable-length data lives on the heap behind a fixed-size pointer, it overlaps safely.

Tagged variants ​

Pair a union with a discriminator field in an enclosing struct, and read the tag first to know which member is valid.

ts
import { Struct, Union } from '@remotex-labs/xstruct';

const payload = new Union<{ raw?: number; text?: string }>({
    raw: 'u32le',
    text: '*utf8'
});

const Packet = new Struct<{ kind: number; payload: { raw?: number; text?: string } }>({
    kind: 'u8',
    payload   
});

const buf = Packet.toBuffer({ kind: 1, payload: { text: 'variable-length' } });
Packet.toObject(buf); // { kind: 1, payload: { ... } }

A union nested in a struct shares the struct's heap, so the pointer payloads of its members are appended to the same region as the rest of the struct's heap data. See Heap & Pointers.

Allowed members ​

MemberAllowed
Primitive, array, bitfieldYes
Fixed-size string (utf8[N])Yes
Pointer / heap-backed (*utf8, ...)Yes
Nested struct or unionYes

See also ​