diff --git a/src/compress.zig b/src/compress.zig index cfac66b..7368251 100644 --- a/src/compress.zig +++ b/src/compress.zig @@ -6,10 +6,10 @@ const std = @import("std"); -// delta compresses an incrementing sorted array of integers using delta -// compression. Sorting is in-place. +// compresses a strictly incrementing sorted slice of integers using delta +// compression. Compression is in-place. pub fn deltaCompress(comptime T: type, elems: []T) error{NotSorted}!void { - if (elems.len == 0) { + if (elems.len <= 1) { return; } var prev: T = elems[0]; @@ -24,14 +24,28 @@ pub fn deltaCompress(comptime T: type, elems: []T) error{NotSorted}!void { } } +// decompresses a slice compressed by deltaCompress. In-place. +pub fn deltaDecompress(comptime T: type, elems: []T) error{Overflow}!void { + if (elems.len <= 1) { + return; + } + + var i: usize = 1; + while (i < elems.len) : (i += 1) { + const x = try std.math.add(T, elems[i - 1], 1); + elems[i] = try std.math.add(T, elems[i], x); + } +} + const testing = std.testing; -test "delta compression positive tests" { +test "delta compress/decompress" { const tests = [_]struct { input: []const u8, want: []const u8 }{ .{ .input = &[_]u8{}, .want = &[_]u8{} }, .{ .input = &[_]u8{0}, .want = &[_]u8{0} }, .{ .input = &[_]u8{10}, .want = &[_]u8{10} }, .{ .input = &[_]u8{ 0, 1, 2 }, .want = &[_]u8{ 0, 0, 0 } }, + .{ .input = &[_]u8{ 10, 20, 30, 255 }, .want = &[_]u8{ 10, 9, 9, 224 } }, .{ .input = &[_]u8{ 0, 254, 255 }, .want = &[_]u8{ 0, 253, 0 } }, }; for (tests) |t| { @@ -44,6 +58,9 @@ test "delta compression positive tests" { try deltaCompress(u8, arr.items); try testing.expectEqualSlices(u8, arr.items, t.want); + + try deltaDecompress(u8, arr.items); + try testing.expectEqualSlices(u8, arr.items, t.input); } } @@ -60,6 +77,18 @@ test "delta compression negative tests" { } } +test "delta decompress overflow" { + for ([_][]const u8{ + &[_]u8{ 255, 0 }, + &[_]u8{ 0, 128, 127 }, + }) |t| { + var arr = try std.ArrayList(u8).initCapacity(testing.allocator, t.len); + defer arr.deinit(); + try arr.appendSlice(t); + try testing.expectError(error.Overflow, deltaDecompress(u8, arr.items)); + } +} + // Represents a variable length integer that we read from a byte stream along // with how many bytes were read to decode it. pub const Varint = struct {