Skip to content

Bitset

The bitset codec handles arrays of boolean values by packing them efficiently into bits. Each byte stores 8 boolean values using LSB-first bit ordering, making it ideal for flags, permissions, and state tracking.

Overview

  • Type: 'bitset'
  • Returns: boolean[]
  • Dependencies: Number codec (uses uint8 internally)
  • Use Cases: Feature flags, permissions, boolean arrays, state indicators

Basic Usage

ts
import type { 
CodecSpec
} from 'binary-codec';
import {
deserialize
,
serialize
} from 'binary-codec';
const
spec
= {
byteLength
: 2,
fields
: [
{
name
: 'flags',
type
: 'bitset',
byteOffset
: 0,
byteLength
: 2 // 2 bytes = 16 bits = 16 booleans
} ] } as
const
satisfies
CodecSpec
;
// Serialize boolean array to buffer const
data
= {
flags
: [
true, false, true, false, // First byte, bits 0-3 true, true, false, false, // First byte, bits 4-7 false, true, false, true, // Second byte, bits 0-3 false, false, true, true // Second byte, bits 4-7 ] }; const
buffer
=
serialize
(
spec
,
data
);
// Deserialize back - hover over result to see the type const
result
=
deserialize
(
spec
,
buffer
);
console
.
log
(
result
.
flags
); // boolean[16]

Field Properties

PropertyTypeRequiredDescription
namestringField identifier
type'bitset'Must be 'bitset'
byteOffsetnumberPosition in buffer (0-based)
byteLengthnumberNumber of bytes (determines bit count)
ts
import type { 
CodecSpec
} from 'binary-codec';
const
spec
= {
byteLength
: 4,
fields
: [
{
name
: 'permissions',
type
: 'bitset',
byteOffset
: 0,
byteLength
: 4 // 4 bytes = 32 bits = 32 booleans
} ] } as
const
satisfies
CodecSpec
;

Bit Ordering (LSB-First)

Bits are ordered from least significant bit (LSB) to most significant bit (MSB) within each byte:

ts
import type { 
CodecSpec
} from 'binary-codec';
import {
serialize
} from 'binary-codec';
const
spec
= {
byteLength
: 1,
fields
: [
{
name
: 'bits',
type
: 'bitset',
byteOffset
: 0,
byteLength
: 1 // 1 byte = 8 bits
} ] } as
const
satisfies
CodecSpec
;
const
data
= {
// LSB-first ordering within the byte:
bits
: [false, true, false, false, true, true, false, true]
// ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ // bit0 bit1 bit2 bit3 bit4 bit5 bit6 bit7 // 0 1 0 0 1 1 0 1 // Binary: 10110010 = 0xB2 = 178 }; const
buffer
=
serialize
(
spec
,
data
);
console
.
log
(
buffer
[0]); // 178 (0xB2 in binary: 10110010)

Multi-Byte Bitsets

For multi-byte bitsets, bits are concatenated by bytes (LSB-first within each byte):

ts
import type { 
CodecSpec
} from 'binary-codec';
import {
deserialize
,
serialize
} from 'binary-codec';
const
spec
= {
byteLength
: 2,
fields
: [
{
name
: 'bits',
type
: 'bitset',
byteOffset
: 0,
byteLength
: 2 // 16 bits total
} ] } as
const
satisfies
CodecSpec
;
const
data
= {
bits
: [
// First byte (bits 0-7) true, false, false, false, false, false, false, false, // Second byte (bits 8-15) false, false, false, false, false, false, false, true ] }; const
buffer
=
serialize
(
spec
,
data
);
console
.
log
(
Array
.
from
(
buffer
)); // [0x01, 0x80]
// First byte: 0x01 (binary: 00000001, LSB=true) // Second byte: 0x80 (binary: 10000000, MSB=true) const
result
=
deserialize
(
spec
,
buffer
);
console
.
log
(
result
.
bits
[0]); // true (first bit of first byte)
console
.
log
(
result
.
bits
[15]); // true (last bit of second byte)

Length Calculation

The number of boolean values equals byteLength × 8:

ts
import type { 
CodecSpec
} from 'binary-codec';
const
examples
= [
{
byteLength
: 1, // 1 × 8 = 8 booleans
name
: 'byte'
}, {
byteLength
: 2, // 2 × 8 = 16 booleans
name
: 'word'
}, {
byteLength
: 4, // 4 × 8 = 32 booleans
name
: 'dword'
} ] as
const
;
const
multiSpec
= {
byteLength
: 7,
fields
: [
{
name
: 'byte',
type
: 'bitset',
byteOffset
: 0,
byteLength
: 1 // 8 booleans
}, {
name
: 'word',
type
: 'bitset',
byteOffset
: 1,
byteLength
: 2 // 16 booleans
}, {
name
: 'dword',
type
: 'bitset',
byteOffset
: 3,
byteLength
: 4 // 32 booleans
} ] } as
const
satisfies
CodecSpec
;
// Type inferred as: // { byte: boolean[], word: boolean[], dword: boolean[] }

Error Handling

The bitset codec performs validation at different stages:

Specification Validation

This validation occurs during both deserialization and serialization:

Error CodeLevelDescription
INVALID_BITSET_LENGTHFATALField byteLength must be positive (> 0)
ts
import type { 
CodecSpec
} from 'binary-codec';
// ❌ This spec will cause FATAL validation error in both operations const
invalidSpec
= {
byteLength
: 4,
fields
: [
{
name
: 'flags',
type
: 'bitset',
byteOffset
: 0,
byteLength
: 0 // INVALID_BITSET_LENGTH: must be > 0
} ] } as
const
satisfies
CodecSpec
;

Data Validation

This validation occurs only during serialization:

Error CodeLevelDescription
INVALID_BITSET_DATA_TYPEFATALData must be an array
BITSET_LENGTH_MISMATCHERRORArray length must equal byteLength × 8
INVALID_BITSET_ELEMENT_TYPEERRORAll array elements must be boolean
ts
import type { 
CodecSpec
} from 'binary-codec';
import {
serialize
} from 'binary-codec';
const
spec
= {
byteLength
: 2,
fields
: [
{
name
: 'flags',
type
: 'bitset',
byteOffset
: 0,
byteLength
: 2 // Expected: 16 booleans
} ] } as
const
satisfies
CodecSpec
;
// ❌ INVALID_BITSET_DATA_TYPE: not an array // const result1 = serialize(spec, { flags: "not array" }) // ❌ BITSET_LENGTH_MISMATCH: wrong length // const result2 = serialize(spec, { flags: [true, false] }) // Only 2, expected 16 // ❌ INVALID_BITSET_ELEMENT_TYPE: not boolean // const result3 = serialize(spec, { flags: Array(16).fill(1) }) // Numbers, not booleans // ✅ Valid data const
result
=
serialize
(
spec
, {
flags
:
Array
.
from
({
length
: 16
}).
fill
(false).
map
((
_
,
i
) =>
i
% 2 === 0)
});

Real-World Examples

Permission System

ts
import type { 
CodecSpec
} from 'binary-codec';
import {
deserialize
,
serialize
} from 'binary-codec';
const
permissionSpec
= {
byteLength
: 1,
fields
: [
{
name
: 'permissions',
type
: 'bitset',
byteOffset
: 0,
byteLength
: 1 // 8 permission flags
} ] } as
const
satisfies
CodecSpec
;
const
userPermissions
= {
permissions
: [
true, // read (bit 0) true, // write (bit 1) false, // execute (bit 2) true, // delete (bit 3) false, // admin (bit 4) false, // modify (bit 5) true, // share (bit 6) false // export (bit 7) ] }; const
buffer
=
serialize
(
permissionSpec
,
userPermissions
);
const
parsed
=
deserialize
(
permissionSpec
,
buffer
);
// Check specific permissions const [
read
,
write
,
execute
,
deletePerm
] =
parsed
.
permissions
;
console
.
log
({
read
,
write
,
execute
,
delete
:
deletePerm
}); // { read: true, write: true, execute: false, delete: true }

Feature Flags Configuration

ts
import type { 
CodecSpec
} from 'binary-codec';
import {
deserialize
,
serialize
} from 'binary-codec';
const
configSpec
= {
byteLength
: 4,
fields
: [
{
name
: 'features',
type
: 'bitset',
byteOffset
: 0,
byteLength
: 4 // 32 feature flags
} ] } as
const
satisfies
CodecSpec
;
// Create feature flags array (32 bits) const
features
=
Array
.
from
({
length
: 32
}).
fill
(false);
features
[0] = true; // darkMode
features
[2] = true; // notifications
features
[5] = true; // analytics
features
[10] = true; // betaFeatures
const
config
= {
features
}; const
buffer
=
serialize
(
configSpec
,
config
);
// Read back and check specific features const
parsed
=
deserialize
(
configSpec
,
buffer
);
const
isDarkModeEnabled
=
parsed
.
features
[0];
const
areNotificationsEnabled
=
parsed
.
features
[2];

Game Progress Tracking

ts
import type { 
CodecSpec
} from 'binary-codec';
const
gameStateSpec
= {
byteLength
: 12,
fields
: [
{
name
: 'levelsCompleted',
type
: 'bitset',
byteOffset
: 0,
byteLength
: 8 // 64 levels
}, {
name
: 'achievementsUnlocked',
type
: 'bitset',
byteOffset
: 8,
byteLength
: 4 // 32 achievements
} ] } as
const
satisfies
CodecSpec
;
// Type inferred as: // { // levelsCompleted: boolean[], // 64 booleans // achievementsUnlocked: boolean[] // 32 booleans // }

Performance Considerations

Bitsets are very memory efficient for boolean arrays:

ts
// Memory comparison for 1000 boolean values:

// Regular boolean array: ~1000 bytes (1 byte per boolean in most JS engines)
const boolArray: boolean[] = Array.from({
  length: 1000
}).fill(false);

// Bitset: 125 bytes (8 booleans per byte)
// byteLength: Math.ceil(1000 / 8) = 125 bytes
const bitsetSpec = {
  byteLength: 125, // 125 × 8 = 1000 bits
  fields: [
    {
      name: 'flags',
      type: 'bitset',
      byteOffset: 0,
      byteLength: 125
    }
  ]
} as const;

// 87.5% memory savings!

Next Steps

Now that you understand the bitset codec, continue with:

Released under the MIT License.