···11+//! Golomb codec implementation for encoding and decoding integers.
22+//! Golomb coding is a lossless, variable-length encoding scheme that is optimal for geometric distributions.
33+44+const std = @import("std");
55+66+const Self = @This();
77+88+/// Golomb parameter M - determines the division point for quotient and remainder
99+m: usize,
1010+1111+/// Internal bit buffer for accumulating bits during encoding/decoding
1212+bit_buffer: u8 = 0,
1313+/// Current bit position within the bit buffer (0-7)
1414+bit_idx: u8 = 0,
1515+/// Current byte position in the buffer
1616+byte_idx: usize = 0,
1717+1818+/// Encodes a value using Golomb coding into the provided buffer.
1919+/// Returns the number of bits written.
2020+pub fn encode(
2121+ self: *Self,
2222+ buffer: []u8,
2323+ value: usize,
2424+ opts: struct {
2525+ write_padding_bits: bool = false,
2626+ reset_tmp_values: bool = true,
2727+ },
2828+) error{BufferTooSmall}!usize {
2929+ if (self.m == 0) @panic("The Golomb parameter M must be larger than 0");
3030+3131+ const b_m = self.bM();
3232+3333+ const q = @divFloor(value, self.m) + 1;
3434+ const b_q = bitLength(q) - 1;
3535+ const r = @rem(value, self.m);
3636+ const b_r = bitLength(r);
3737+3838+ const needed_bits = b_q + b_q + 1 + b_m;
3939+ const buffer_len_bits = needed_bits + self.byte_idx * 8 + self.bit_idx;
4040+4141+ if (buffer_len_bits > buffer.len * 8) return error.BufferTooSmall;
4242+4343+ // Write q
4444+ for (0..b_q) |_| {
4545+ self.writeBit(buffer, 0);
4646+ }
4747+ self.writeBits(buffer, q, b_q + 1);
4848+4949+ // Write r
5050+ for (0..(b_m - b_r)) |_| {
5151+ self.writeBit(buffer, 0);
5252+ }
5353+ self.writeBits(buffer, r, b_r);
5454+5555+ // Write padding bits
5656+ if (opts.write_padding_bits) {
5757+ const padding = buffer.len * 8 - buffer_len_bits;
5858+5959+ for (0..padding) |_| {
6060+ self.writeBit(buffer, 0);
6161+ }
6262+6363+ std.debug.assert(self.bit_buffer == 0);
6464+ }
6565+6666+ // Reset helper vars
6767+ if (opts.reset_tmp_values) {
6868+ self.reset();
6969+ }
7070+7171+ return buffer_len_bits;
7272+}
7373+7474+/// Decodes a Golomb-encoded value from the buffer.
7575+/// Returns the decoded value.
7676+pub fn decode(
7777+ self: *Self,
7878+ buffer: []const u8,
7979+ opts: struct { reset_tmp_values: bool = true },
8080+) error{InvalidFormat}!usize {
8181+ if (self.m == 0) @panic("The Golomb parameter M must be larger than 0");
8282+8383+ const b_m = self.bM();
8484+8585+ var q: usize = 0;
8686+ var b_q: u8 = 0;
8787+ var r: usize = 0;
8888+8989+ // Read b_q
9090+ while (self.readBit(buffer)) |bit| {
9191+ if (bit == 0) {
9292+ b_q += 1;
9393+ } else if (bit == 1) {
9494+ q = 1;
9595+ break;
9696+ } else {
9797+ return error.InvalidFormat;
9898+ }
9999+ }
100100+101101+ // Read q
102102+ q <<= @as(u6, @intCast(b_q));
103103+ q |= self.readBits(buffer, b_q) catch return error.InvalidFormat;
104104+ q -= 1;
105105+106106+ // Read r
107107+ r = self.readBits(buffer, b_m) catch return error.InvalidFormat;
108108+109109+ // Reset helper vars
110110+ if (opts.reset_tmp_values) {
111111+ self.reset();
112112+ }
113113+114114+ return q * self.m + r;
115115+}
116116+117117+/// Resets internal state variables used during encoding/decoding.
118118+pub fn reset(self: *Self) void {
119119+ self.bit_buffer = 0;
120120+ self.bit_idx = 0;
121121+ self.byte_idx = 0;
122122+}
123123+124124+/// Calculates the number of bits needed to represent the remainder in Golomb coding.
125125+fn bM(self: *const Self) u8 {
126126+ const b = bitLength(self.m);
127127+ return if (isPowerOfTwo(self.m)) b - 1 else b;
128128+}
129129+130130+/// Writes a single bit to the buffer.
131131+fn writeBit(self: *Self, buffer: []u8, bit: u8) void {
132132+ self.bit_buffer = (self.bit_buffer << 1) | (bit & 1);
133133+ self.bit_idx += 1;
134134+135135+ if (self.bit_idx == 8) {
136136+ buffer[self.byte_idx] = self.bit_buffer;
137137+ self.byte_idx += 1;
138138+ self.bit_buffer = 0;
139139+ self.bit_idx = 0;
140140+ }
141141+}
142142+143143+/// Writes multiple bits from a value to the buffer.
144144+fn writeBits(self: *Self, buffer: []u8, value: usize, count: u8) void {
145145+ var i = count;
146146+ while (i > 0) {
147147+ i -= 1;
148148+ const bit = @as(u8, @intCast((value >> @as(u6, @intCast(i))) & 1));
149149+ self.writeBit(buffer, bit);
150150+ }
151151+}
152152+153153+/// Reads a single bit from the buffer. Returns null if at end of buffer.
154154+fn readBit(self: *Self, buffer: []const u8) ?u8 {
155155+ if (self.byte_idx > buffer.len) return null;
156156+157157+ const bit = (buffer[self.byte_idx] >> @as(u3, @intCast(7 - self.bit_idx))) & 1;
158158+ self.bit_idx += 1;
159159+160160+ if (self.bit_idx == 8) {
161161+ self.byte_idx += 1;
162162+ self.bit_idx = 0;
163163+ }
164164+165165+ return bit;
166166+}
167167+168168+/// Reads multiple bits from the buffer and returns them as a value.
169169+fn readBits(self: *Self, buffer: []const u8, count: u8) !usize {
170170+ var result: usize = 0;
171171+172172+ for (0..count) |_| {
173173+ const bit = self.readBit(buffer) orelse return error.OutOfBounds;
174174+ result = (result << 1) | @as(usize, bit);
175175+ }
176176+177177+ return result;
178178+}
179179+180180+/// Calculates the number of bits required to represent a value.
181181+fn bitLength(value: anytype) u8 {
182182+ return @bitSizeOf(@TypeOf(value)) - @clz(value);
183183+}
184184+185185+/// Checks if a value is a power of two.
186186+fn isPowerOfTwo(value: usize) bool {
187187+ return (value & (value - 1)) == 0;
188188+}
189189+190190+test "encode val = 42, m = 8" {
191191+ const testing = std.testing;
192192+193193+ var gol = Self{ .m = 8 };
194194+ const input: usize = 42;
195195+ var encoded: [1]u8 = undefined;
196196+ _ = try gol.encode(&encoded, input, .{});
197197+ try testing.expectEqualSlices(u8, &.{50}, &encoded);
198198+}
199199+200200+test "decode val = {50}, m = 8" {
201201+ const testing = std.testing;
202202+203203+ var gol = Self{ .m = 8 };
204204+ const input = &[_]u8{50};
205205+ const decoded = try gol.decode(input, .{});
206206+207207+ try testing.expectEqual(42, decoded);
208208+}
209209+210210+test "encode + decode val = 1564, m = 457" {
211211+ const testing = std.testing;
212212+213213+ var gol = Self{ .m = 457 };
214214+ const input: usize = 1564;
215215+ var encoded: [2]u8 = undefined;
216216+217217+ _ = try gol.encode(
218218+ &encoded,
219219+ input,
220220+ .{ .write_padding_bits = true },
221221+ );
222222+ try testing.expectEqualSlices(u8, &.{ 35, 4 }, &encoded);
223223+224224+ const decoded = try gol.decode(&encoded, .{});
225225+ try testing.expectEqual(input, decoded);
226226+}
227227+228228+test "encode multiple val = { 1564, 42 }, m = 457" {
229229+ const testing = std.testing;
230230+231231+ var gol = Self{ .m = 457 };
232232+ const input = [_]usize{ 1564, 42 };
233233+ var encoded: [3]u8 = undefined;
234234+235235+ _ = try gol.encode(&encoded, input[0], .{ .reset_tmp_values = false });
236236+ _ = try gol.encode(&encoded, input[1], .{});
237237+238238+ try testing.expectEqualSlices(u8, &.{ 35, 6, 42 }, &encoded);
239239+}
240240+241241+test "decode multiple val = { 35, 6, 8 }, m = 457" {
242242+ const testing = std.testing;
243243+244244+ var gol = Self{ .m = 457 };
245245+ const input = &[_]u8{ 35, 6, 42 };
246246+247247+ const decoded1 = try gol.decode(input, .{ .reset_tmp_values = false });
248248+ try testing.expectEqual(1564, decoded1);
249249+250250+ const decoded2 = try gol.decode(input, .{});
251251+ try testing.expectEqual(42, decoded2);
252252+}
+3-18
src/root.zig
···11//! By convention, root.zig is the root source file when making a library.
22const std = @import("std");
3344-pub fn bufferedPrint() !void {
55- // Stdout is for the actual output of your application, for example if you
66- // are implementing gzip, then only the compressed bytes should be sent to
77- // stdout, not any debugging messages.
88- var stdout_buffer: [1024]u8 = undefined;
99- var stdout_writer = std.fs.File.stdout().writer(&stdout_buffer);
1010- const stdout = &stdout_writer.interface;
44+pub const Golomb = @import("Golomb.zig");
1151212- try stdout.print("Run `zig build test` to run the tests.\n", .{});
1313-1414- try stdout.flush(); // Don't forget to flush!
1515-}
1616-1717-pub fn add(a: i32, b: i32) i32 {
1818- return a + b;
1919-}
2020-2121-test "basic add functionality" {
2222- try std.testing.expect(add(3, 7) == 10);
66+test {
77+ std.testing.refAllDecls(@This());
238}