const std = @import("std"); const expect = std.testing.expect; const io = std.io; const mem = std.mem; const testing = std.testing; const ArrayList = std.ArrayList; const deflate = @import("compressor.zig"); const inflate = @import("decompressor.zig"); const deflate_const = @import("deflate_const.zig"); test "best speed" { // Tests that round-tripping through deflate and then inflate recovers the original input. // The Write sizes are near the thresholds in the compressor.encSpeed method (0, 16, 128), as well // as near `deflate_const.max_store_block_size` (65535). var abcabc = try testing.allocator.alloc(u8, 131_072); defer testing.allocator.free(abcabc); for (abcabc) |_, i| { abcabc[i] = @intCast(u8, i % 128); } var tc_01 = [_]u32{ 65536, 0 }; var tc_02 = [_]u32{ 65536, 1 }; var tc_03 = [_]u32{ 65536, 1, 256 }; var tc_04 = [_]u32{ 65536, 1, 65536 }; var tc_05 = [_]u32{ 65536, 14 }; var tc_06 = [_]u32{ 65536, 15 }; var tc_07 = [_]u32{ 65536, 16 }; var tc_08 = [_]u32{ 65536, 16, 256 }; var tc_09 = [_]u32{ 65536, 16, 65536 }; var tc_10 = [_]u32{ 65536, 127 }; var tc_11 = [_]u32{ 65536, 127 }; var tc_12 = [_]u32{ 65536, 128 }; var tc_13 = [_]u32{ 65536, 128, 256 }; var tc_14 = [_]u32{ 65536, 128, 65536 }; var tc_15 = [_]u32{ 65536, 129 }; var tc_16 = [_]u32{ 65536, 65536, 256 }; var tc_17 = [_]u32{ 65536, 65536, 65536 }; var test_cases = [_][]u32{ &tc_01, &tc_02, &tc_03, &tc_04, &tc_05, &tc_06, &tc_07, &tc_08, &tc_09, &tc_10, &tc_11, &tc_12, &tc_13, &tc_14, &tc_15, &tc_16, &tc_17, }; for (test_cases) |tc| { var firsts = [_]u32{ 1, 65534, 65535, 65536, 65537, 131072 }; for (firsts) |first_n| { tc[0] = first_n; var to_flush = [_]bool{ false, true }; for (to_flush) |flush| { var compressed = ArrayList(u8).init(testing.allocator); defer compressed.deinit(); var want = ArrayList(u8).init(testing.allocator); defer want.deinit(); var comp = try deflate.compressor( testing.allocator, compressed.writer(), .{ .level = .best_speed }, ); defer comp.deinit(); for (tc) |n| { try want.appendSlice(abcabc[0..n]); try comp.writer().writeAll(abcabc[0..n]); if (flush) { try comp.flush(); } } try comp.close(); var decompressed = try testing.allocator.alloc(u8, want.items.len); defer testing.allocator.free(decompressed); var fib = io.fixedBufferStream(compressed.items); var decomp = try inflate.decompressor(testing.allocator, fib.reader(), null); defer decomp.deinit(); var read = try decomp.reader().readAll(decompressed); _ = decomp.close(); try testing.expectEqual(want.items.len, read); try testing.expectEqualSlices(u8, want.items, decompressed); } } } } test "best speed max match offset" { const abc = "abcdefgh"; const xyz = "stuvwxyz"; const input_margin = 16 - 1; const match_before = [_]bool{ false, true }; for (match_before) |do_match_before| { const extras = [_]u32{ 0, input_margin - 1, input_margin, input_margin + 1, 2 * input_margin, }; for (extras) |extra| { var offset_adj: i32 = -5; while (offset_adj <= 5) : (offset_adj += 1) { var offset = deflate_const.max_match_offset + offset_adj; // Make src to be a []u8 of the form // fmt("{s}{s}{s}{s}{s}", .{abc, zeros0, xyzMaybe, abc, zeros1}) // where: // zeros0 is approximately max_match_offset zeros. // xyzMaybe is either xyz or the empty string. // zeros1 is between 0 and 30 zeros. // The difference between the two abc's will be offset, which // is max_match_offset plus or minus a small adjustment. var src_len: usize = @intCast(usize, offset + @as(i32, abc.len) + @intCast(i32, extra)); var src = try testing.allocator.alloc(u8, src_len); defer testing.allocator.free(src); mem.copy(u8, src, abc); if (!do_match_before) { var src_offset: usize = @intCast(usize, offset - @as(i32, xyz.len)); mem.copy(u8, src[src_offset..], xyz); } var src_offset: usize = @intCast(usize, offset); mem.copy(u8, src[src_offset..], abc); var compressed = ArrayList(u8).init(testing.allocator); defer compressed.deinit(); var comp = try deflate.compressor( testing.allocator, compressed.writer(), .{ .level = .best_speed }, ); defer comp.deinit(); try comp.writer().writeAll(src); _ = try comp.close(); var decompressed = try testing.allocator.alloc(u8, src.len); defer testing.allocator.free(decompressed); var fib = io.fixedBufferStream(compressed.items); var decomp = try inflate.decompressor(testing.allocator, fib.reader(), null); defer decomp.deinit(); var read = try decomp.reader().readAll(decompressed); _ = decomp.close(); try testing.expectEqual(src.len, read); try testing.expectEqualSlices(u8, src, decompressed); } } } }