Files
skills/docs/foundation/bitpacker/bitpacker-delta/src/delta-state/delta-state.md
2026-05-29 08:20:56 +01:00

3.9 KiB

DeltaStateManager

(no submodules)

Purpose

DeltaStateManager keeps deep-cloned snapshots of previous values per state key, so that a delta encoder can diff the current value against its predecessor without worrying about caller-side mutations. It is the storage layer for DeltaManager; consumers usually go through DeltaManager rather than calling this class directly.

The store is bounded: an LRU eviction policy drops the oldest key once the configured capacity is reached. Default capacity is 100 keys, which is plenty for most workloads (one key per per-peer state stream). Re-storing under an existing key updates the value in place without advancing its LRU position — this matches upstream semantics so that frequently-updated keys do not unfairly evict rarely-updated ones.

Features

  • Deep-cloning on store: Date, all typed-array variants (Uint8Array, Int8Array, Uint16Array, Int16Array, Uint32Array, Int32Array, Float32Array, Float64Array), arrays, and plain objects are all isolated from caller mutations.
  • Insertion-order LRU eviction; maxStates configurable (default 100).
  • clear(), clearState(key), getStateCount(), getMaxStates() accessors for inspection and reset.
  • Zero Math.random / Date.now calls — fully deterministic given identical inputs.

Usage: Basic Example

import { DeltaStateManager } from "@suaveplan/bitpacker-delta";

const states = new DeltaStateManager(100);

states.storePreviousState("player-1", { x: 0, y: 0 });
const previous = states.getPreviousState("player-1");
// → { x: 0, y: 0 }

Usage: Advanced Example

import { DeltaStateManager } from "@suaveplan/bitpacker-delta";

const states = new DeltaStateManager(3);
states.storePreviousState("a", { v: 1 });
states.storePreviousState("b", { v: 2 });
states.storePreviousState("c", { v: 3 });
states.storePreviousState("d", { v: 4 }); // evicts "a"

states.hasPreviousState("a"); // → false (evicted)
states.hasPreviousState("d"); // → true

// Mutating the original after storing does NOT touch the snapshot:
const original = { x: 0 };
states.storePreviousState("e", original);
original.x = 999;
states.getPreviousState("e"); // → { x: 0 }

API Reference

new DeltaStateManager(maxStates?: number)

  • maxStates — positive integer; default 100. Throws RangeError if non-integer or < 1.

storePreviousState(key: string, state: unknown): void

Stores a deep-cloned copy of state under key. If key already exists, the value is updated in place; LRU position is unchanged. If the store is at capacity and this is a new key, the oldest key is evicted.

getPreviousState(key: string): unknown | undefined

Returns the stored snapshot, or undefined if the key has never been stored or has been evicted.

hasPreviousState(key: string): boolean

True if a snapshot exists for key.

clear(): void

Drops every snapshot.

clearState(key: string): void

Drops the snapshot for one key. No-op if absent.

getStateCount(): number

Number of stored snapshots.

getMaxStates(): number

Configured maximum capacity.

Implementation Notes

The deep-clone walker handles the eight typed-array variants explicitly because the generic object-clone path (Object.hasOwn over keys) does not preserve Uint8Array-vs-plain-array distinction. Maps and Sets are intentionally NOT cloned: delta-state semantics assume keys are strings and values are plain data; if a consumer needs Map/Set, they should canonicalise to a plain object first.

Performance: insertion is O(1) amortised; eviction is O(1) (Array .shift() on a small bounded array). Lookup is O(1). The stateKeys array tracks insertion order without using a Map's native iteration (some legacy environments don't preserve insertion order — though every modern runtime does — so the explicit array is defensive).