Files
suaveplan/packages/media/audio/audio-metadata/src/utils/buffer-utils.md
2026-05-27 23:47:53 +01:00

3.7 KiB
Raw Blame History

buffer-utils

(no submodules)

Purpose

Provides a compact set of pure binary-reading helpers that operate directly on Uint8Array values. These utilities abstract away the bit-shifting and byte-ordering arithmetic that every audio-format parser needs, reducing the risk of off-by-one errors and making parser code easier to read and test in isolation.

All helpers are environment-agnostic — they use only the Uint8Array and TextDecoder web-standard APIs, so they work identically in Node.js 18+, Deno, and modern browsers without any polyfills.

Features

  • Unsigned/signed integer readers: readUint8, readUint16BE, readUint16LE, readUint32BE, readUint32LE, readInt32BE
  • String decoder with "utf8" (TextDecoder) and "latin1" (char-code loop) encodings
  • readSyncsafeInt for ID3v2 syncsafe 28-bit integers
  • concatBuffers for building test fixtures and merging partial reads
  • Every function returns a sensible default (0 / "") on out-of-bounds access rather than throwing

Usage: Basic Example

import {
  readUint32BE,
  readString,
} from "@suaveplan/audio-metadata";

const buf = new Uint8Array([0x00, 0x00, 0x04, 0x00]);
const size = readUint32BE(buf, 0); // 1024

const text = new Uint8Array([0x48, 0x69]);
readString(text, 0, 2); // "Hi"

Usage: Advanced Example

import {
  readSyncsafeInt,
  readUint16BE,
  concatBuffers,
} from "@suaveplan/audio-metadata";

// Decode an ID3v2 tag size from a header
const header = new Uint8Array([0x49, 0x44, 0x33, 0x03, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01]);
const tagSize = readSyncsafeInt(header, 6); // 257

// Build a larger test buffer by concatenating parts
const part1 = new Uint8Array([0x52, 0x49, 0x46, 0x46]);
const part2 = new Uint8Array([0x00, 0x10, 0x00, 0x00]);
const combined = concatBuffers(part1, part2); // "RIFF" + size bytes

API Reference

readUint8(buf, offset)

Returns buf[offset] as a number (0–255), or 0 if out of bounds.

readUint16BE(buf, offset)

Reads two bytes at offset as a big-endian unsigned 16-bit integer.

readUint16LE(buf, offset)

Reads two bytes at offset as a little-endian unsigned 16-bit integer.

readUint32BE(buf, offset)

Reads four bytes at offset as a big-endian unsigned 32-bit integer.

readUint32LE(buf, offset)

Reads four bytes at offset as a little-endian unsigned 32-bit integer.

readInt32BE(buf, offset)

Reads four bytes at offset as a big-endian signed 32-bit integer. Converts unsigned values ≥ 0x80000000 to their negative two's-complement equivalents.

readString(buf, offset, length, encoding?)

Decodes length bytes starting at offset.

  • encoding = "utf8" (default): uses TextDecoder("utf-8") for correct multi-byte handling.
  • encoding = "latin1": maps each byte to its Unicode code point — necessary for ID3v1 and some legacy ID3v2 fields.

readSyncsafeInt(buf, offset)

Reads four bytes at offset and decodes them as an ID3v2 syncsafe integer (28-bit value, MSB of each byte ignored). Returns a plain number.

concatBuffers(a, b)

Returns a new Uint8Array containing all bytes of a followed by all bytes of b.

Implementation Notes

The >>> 0 cast after bit-shifting operations ensures the result is treated as an unsigned 32-bit integer in JavaScript, preventing sign-extension surprises with values whose MSB is set. The readUint32BE implementation uses multiplication by 0x1000000 instead of << 24 to avoid the signed-integer trap that << 24 introduces for bytes with the high bit set. Out-of-bounds array reads return undefined in JavaScript, which is coerced to 0 via the ?? 0 fallback — this trades silent safety for parser robustness with truncated buffers.