|
| 1 | +const std = @import("std"); |
| 2 | +const flate = @import("flate"); |
| 3 | +const c = @cImport(@cInclude("puff.h")); |
| 4 | + |
| 5 | +pub export fn main() void { |
| 6 | + zigMain() catch unreachable; |
| 7 | +} |
| 8 | + |
| 9 | +pub fn zigMain() !void { |
| 10 | + var gpa = std.heap.GeneralPurposeAllocator(.{}){}; |
| 11 | + defer std.debug.assert(gpa.deinit() == .ok); |
| 12 | + const allocator = gpa.allocator(); |
| 13 | + |
| 14 | + // Read the data from stdin |
| 15 | + const stdin = std.io.getStdIn(); |
| 16 | + const data = try stdin.readToEndAlloc(allocator, std.math.maxInt(usize)); |
| 17 | + defer allocator.free(data); |
| 18 | + |
| 19 | + // Try to parse the data with puff |
| 20 | + var puff_error: anyerror = error.NoError; |
| 21 | + const inflated_puff: ?[]u8 = puffAlloc(allocator, data) catch |err| blk: { |
| 22 | + puff_error = err; |
| 23 | + break :blk null; |
| 24 | + }; |
| 25 | + defer if (inflated_puff != null) { |
| 26 | + allocator.free(inflated_puff.?); |
| 27 | + }; |
| 28 | + |
| 29 | + var fbs = std.io.fixedBufferStream(data); |
| 30 | + const reader = fbs.reader(); |
| 31 | + var inflate = flate.raw.decompressor(reader); |
| 32 | + |
| 33 | + var zig_error: anyerror = error.NoError; |
| 34 | + const inflated: ?[]u8 = inflate.reader().readAllAlloc(allocator, std.math.maxInt(usize)) catch |err| blk: { |
| 35 | + zig_error = err; |
| 36 | + break :blk null; |
| 37 | + }; |
| 38 | + defer if (inflated != null) { |
| 39 | + allocator.free(inflated.?); |
| 40 | + }; |
| 41 | + |
| 42 | + if (inflated_puff == null or inflated == null) { |
| 43 | + //std.debug.print("puff error: {}, zig error: {}\n", .{ puff_error, zig_error }); |
| 44 | + |
| 45 | + if (inflated_puff != null or inflated != null) { |
| 46 | + return error.MismatchedErrors; |
| 47 | + } |
| 48 | + } else { |
| 49 | + try std.testing.expectEqualSlices(u8, inflated_puff.?, inflated.?); |
| 50 | + } |
| 51 | +} |
| 52 | + |
| 53 | +fn puffAlloc(allocator: std.mem.Allocator, input: []const u8) ![]u8 { |
| 54 | + // call once to get the uncompressed length |
| 55 | + var decoded_len: c_ulong = undefined; |
| 56 | + var source_len: c_ulong = input.len; |
| 57 | + const result = c.puff(c.NIL, &decoded_len, input.ptr, &source_len); |
| 58 | + |
| 59 | + if (result != 0) { |
| 60 | + return translatePuffError(result); |
| 61 | + } |
| 62 | + |
| 63 | + const dest = try allocator.alloc(u8, decoded_len); |
| 64 | + errdefer allocator.free(dest); |
| 65 | + |
| 66 | + // call again to actually get the output |
| 67 | + _ = c.puff(dest.ptr, &decoded_len, input.ptr, &source_len); |
| 68 | + return dest; |
| 69 | +} |
| 70 | + |
| 71 | +fn translatePuffError(code: c_int) anyerror { |
| 72 | + return switch (code) { |
| 73 | + 2 => error.EndOfStream, |
| 74 | + 1 => error.OutputSpaceExhausted, |
| 75 | + 0 => unreachable, |
| 76 | + -1 => error.InvalidBlockType, |
| 77 | + -2 => error.StoredBlockLengthNotOnesComplement, |
| 78 | + -3 => error.TooManyLengthOrDistanceCodes, |
| 79 | + -4 => error.CodeLengthsCodesIncomplete, |
| 80 | + -5 => error.RepeatLengthsWithNoFirstLengths, |
| 81 | + -6 => error.RepeatMoreThanSpecifiedLengths, |
| 82 | + -7 => error.InvalidLiteralOrLengthCodeLengths, |
| 83 | + -8 => error.InvalidDistanceCodeLengths, |
| 84 | + -9 => error.MissingEOBCode, |
| 85 | + -10 => error.InvalidLiteralOrLengthOrDistanceCodeInBlock, |
| 86 | + -11 => error.DistanceTooFarBackInBlock, |
| 87 | + else => unreachable, |
| 88 | + }; |
| 89 | +} |
0 commit comments