/** * RFC 1982 serial-number arithmetic for the 32-bit MMTP AL-FEC symbol * counters (ISO/IEC 23008-1:2023 C.5.2 SS_ID, C.5.3 SS_Start). * * SS_ID is a 32-bit field, so a stream that runs long enough wraps it to 0. * Ordering two SS_IDs (or two blocks by their first SS_ID) with `<` reads * every post-wrap value as older than every pre-wrap one. Serial order * agrees with `<` whenever the two values are less than 2^31 apart, so for * every stream that has not reached the wrap the result is unchanged; only * the order across the wrap point differs (BLO-35597 iso:5). */ /** 2^SERIAL_BITS for SERIAL_BITS = 32. */ export const SERIAL32_SPAN = 0x1_0000_0000 /** RFC 1982 §3.2's 2^(SERIAL_BITS - 1). */ export const SERIAL32_HALF = 0x8000_0000 function assertUint32(value: number, name: string): void { if (!Number.isInteger(value) || value < 0 || value >= SERIAL32_SPAN) { throw new RangeError(`${name} must be a uint32 (got ${value})`) } } /** * RFC 1982 §3.1 addition, extended to negative `n` for the matching * subtraction: `(s + n) mod 2^32`. `n` must lie in (-2^31, 2^31). */ export function serial32Add(s: number, n: number): number { assertUint32(s, 's') if (!Number.isInteger(n) || n <= -SERIAL32_HALF || n >= SERIAL32_HALF) { throw new RangeError(`serial addend must lie in (-2^31, 2^31) (got ${n})`) } return (s + n + SERIAL32_SPAN) % SERIAL32_SPAN } /** Forward distance from `from` to `to`: `(to - from) mod 2^32`. */ export function serial32Distance(from: number, to: number): number { return (to - from) >>> 0 } /** * RFC 1982 §3.2: `a` precedes `b`. Values exactly 2^31 apart are unordered * (neither precedes the other). */ export function serial32Precedes(a: number, b: number): boolean { const distance = serial32Distance(a, b) return distance !== 0 && distance < SERIAL32_HALF }