Files
smart-rgb/frontend/src/shared/utils/binaryDecoding.ts
2025-10-12 22:12:23 -05:00

60 lines
1.8 KiB
TypeScript

/**
* Binary decoding utilities for territory snapshot data from Rust backend.
*
* The TerritorySnapshot uses a sparse binary format to minimize data transfer:
* - Only claimed tiles (owner_id != 0) are transmitted
* - All other tiles default to unclaimed (owner_id = 0)
* - Binary format: [count:4][tile_changes...]
* where each change is [index:4][owner:2] (6 bytes per claimed tile)
*/
export interface TileOwnership {
index: number;
owner_id: number;
}
/**
* Decode sparse binary territory snapshot from Rust backend.
*
* @param data Binary data array (serialized from Vec<u8> in Rust)
* @returns Array of claimed tiles with indices and owner IDs, or null if invalid
*/
export function decodeTerritorySnapshot(data: number[] | Uint8Array): TileOwnership[] | null {
// Convert to Uint8Array if needed for faster access
const bytes = data instanceof Uint8Array ? data : new Uint8Array(data);
if (bytes.length < 4) {
console.error("Invalid territory snapshot: not enough data for count");
return null;
}
// Use DataView for faster little-endian reads
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
// Read count (4 bytes, little-endian u32)
const count = view.getUint32(0, true);
const expectedSize = 4 + count * 6;
if (bytes.length !== expectedSize) {
console.error(
`Invalid territory snapshot: expected ${expectedSize} bytes, got ${bytes.length}`
);
return null;
}
const tiles: TileOwnership[] = new Array(count);
for (let i = 0; i < count; i++) {
const offset = 4 + i * 6;
// Read index (4 bytes, little-endian u32)
const index = view.getUint32(offset, true);
// Read owner_id (2 bytes, little-endian u16)
const owner_id = view.getUint16(offset + 4, true);
tiles[i] = { index, owner_id };
}
return tiles;
}