bitread

tape
import bitread from "bitread";

// An ADTS header starts with 12 bits of sync word, then 1 bit of MPEG version
let frame: []const u8 = "\xFF\xF1\x50\x80";
var r: bitread.Reader = bitread.reader(frame);
let sync: i64 = r.read(12);         // 0xFFF
let version: i64 = r.read(1);       // 0: MPEG-4
r.skip(3);                          // layer, protection_absent
let profile: i64 = r.read(2);       // 1: AAC-LC
if (r.bad) { /* the header was cut short */ }

bitread reads bits most significant first: the first bit of a byte is its highest. That is the order AAC, H.264, MP4’s descriptors and most media formats use; deflate and brotli read the other way round, with lz’s BitIn. aac, h264 and mp4 are built on it.

bitread is a pure module, freestanding (t0): nothing allocates.

peek

tape
pub fn peek(d: []const u8, pos: i64, n: i64) -> i64

The n bits of d starting at bit pos (bit 0 is the highest bit of d[0]), as a number whose highest bit is the first one read. Bits past the end of d read as zeros, so a decoder can look ahead at the longest code it knows without checking how much is left.

tape
let d: []const u8 = "\xA5\x0F";
bitread.peek(d, 0, 4);      // 0xA
bitread.peek(d, 4, 8);      // 0x50
bitread.peek(d, 12, 8);     // 0xF0: the last four bits are past the end

n is 0 to 57. It traps when n is outside that range or pos is negative.

Reader

tape
pub struct Reader { d: []const u8; pos: i64; end: i64; bad: bool; }
pub fn reader(d: []const u8) -> Reader

A position in a buffer: pos counts bits from the start, end is where the data ends (d.len * 8). Reading past the end does not trap: the missing bits are zeros and bad becomes true and stays true. Read a whole unit (a header, a block) and check bad once at the end.

MethodWhat it does
r.peek(n)The next n bits (0..57), without moving
r.read(n)The next n bits (0..57), taken
r.skip(n)Moves n bits on
r.align()Moves on to the next byte boundary; nothing when at one
r.left()The bits left: end - pos (negative after reading past the end)

peek, read and skip are inlined: use them in decoding loops.

ErrMsg

tape
pub struct ErrMsg { buf: [200]u8; len: i64; }
pub fn ErrMsg.set(*self, what: []const u8)
pub fn ErrMsg.text(*const self) -> []const u8

The text of a codec’s last error, kept without a heap: set keeps the first 200 bytes of what, text returns them (empty before any set). A decoder returns an error enum and keeps the details here, for a last_error() method:

tape
pub struct Decoder { err: bitread.ErrMsg; }

fn Decoder.fail(*self, e: MyError, what: []const u8) -> MyError {
    self.err.set(what);
    return e;
}

pub fn Decoder.last_error(*const self) -> []const u8 { return self.err.text(); }

Last modified: