Something went wrong. Try again.
An MPEG audio codec in Zig that decodes Layers I, II and III and encodes Layers II and III.
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119// SPDX-FileCopyrightText: © 2026 Jeffrey C. Ollie <jeff@ocjtech.us>// SPDX-License-Identifier: MIT
//! A most-significant-bit-first bit writer: the mirror of `bitstream.zig`.//!//! Writes past the end of the buffer are dropped rather than reported, and//! counted, in the same spirit as the reader's reads past the end returning//! zeroes. An encoder that has worked out its bit budget correctly never//! overruns, and one that has not should fail its own check rather than//! scribble past the end of a frame.
const std = @import("std");
const Writer = @This();
bytes: []u8,/// Write position, in bits from the start of `bytes`.pos: usize = 0,/// Bits that would have gone past the end of `bytes`.overrun: usize = 0,
pub fn init(bytes: []u8) Writer { @memset(bytes, 0); return .{ .bytes = bytes };}
/// Write the low `n` bits of `value`, most significant first.pub fn write(self: *Writer, value: u32, n: u6) void { var left = n; while (left > 0) { left -= 1; const bit: u1 = @truncate(value >> @intCast(left)); self.writeBit(bit); }}
pub fn writeBit(self: *Writer, bit: u1) void { if (self.pos >= self.bytes.len * 8) { self.overrun += 1; return; } // The buffer starts zeroed and is written once, so setting the one bits // is enough and there is nothing to clear. if (bit != 0) { self.bytes[self.pos / 8] |= @as(u8, 0x80) >> @intCast(self.pos % 8); } self.pos += 1;}
/// Bits between the write position and the end of the buffer, saturating at/// zero once the writer has overrun.pub fn remaining(self: Writer) usize { const total = self.bytes.len * 8; return if (self.pos >= total) 0 else total - self.pos;}
/// Advance to the next byte boundary, leaving zeroes behind.pub fn alignToByte(self: *Writer) void { while (self.pos % 8 != 0) self.writeBit(0);}
const testing = std.testing;const Reader = @import("bitstream.zig");
test "what is written reads back" { var buffer: [64]u8 = undefined; var writer = Writer.init(&buffer);
var prng = std.Random.DefaultPrng.init(0xb175); const random = prng.random(); var widths: [40]u6 = undefined; var values: [40]u32 = undefined; var total: usize = 0; for (&widths, &values) |*width, *value| { width.* = random.intRangeAtMost(u6, 1, 24); value.* = random.int(u32) & (@as(u32, 0xffff_ffff) >> @intCast(32 - @as(u7, width.*))); if (total + width.* > buffer.len * 8) { width.* = 0; value.* = 0; } total += width.*; writer.write(value.*, width.*); } try testing.expectEqual(@as(usize, 0), writer.overrun); try testing.expectEqual(total, writer.pos);
var reader = Reader.init(&buffer); for (widths, values) |width, value| { try testing.expectEqual(value, reader.read(width)); }}
test "bits land most significant first" { var buffer: [2]u8 = undefined; var writer = Writer.init(&buffer); writer.write(0b1011, 4); writer.write(0b0001, 4); try testing.expectEqual(@as(u8, 0b1011_0001), buffer[0]);}
test "writing past the end is counted, not written" { var buffer: [1]u8 = undefined; var writer = Writer.init(&buffer); writer.write(0xff, 8); try testing.expectEqual(@as(usize, 0), writer.remaining()); writer.write(0xff, 5); try testing.expectEqual(@as(usize, 5), writer.overrun); try testing.expectEqual(@as(u8, 0xff), buffer[0]);}
test "aligning writes zeroes to the byte boundary" { var buffer: [2]u8 = undefined; var writer = Writer.init(&buffer); writer.write(0b111, 3); writer.alignToByte(); try testing.expectEqual(@as(usize, 8), writer.pos); try testing.expectEqual(@as(u8, 0b1110_0000), buffer[0]);}