diff --git a/flake.lock b/flake.lock index dde7ff5..bb364a5 100644 --- a/flake.lock +++ b/flake.lock @@ -1,5 +1,29 @@ { "nodes": { + "binned_allocator": { + "flake": false, + "locked": { + "narHash": "sha256-FhnSM7KbQSOg9jMiJxEpXJbPX4uvkLzFbG5DaGlgYrc=", + "type": "tarball", + "url": "https://gist.github.com/silversquirl/c1e4840048fdf48e669b6eac76d80634/archive/8bbe137e65f26854ff936046d884a45d4fa156de.tar.gz" + }, + "original": { + "type": "tarball", + "url": "https://gist.github.com/silversquirl/c1e4840048fdf48e669b6eac76d80634/archive/8bbe137e65f26854ff936046d884a45d4fa156de.tar.gz" + } + }, + "diffz": { + "flake": false, + "locked": { + "narHash": "sha256-CHRuEEeYGEpotuPo4vRe1qKz0EmkZwVvNjZagFj07ok=", + "type": "tarball", + "url": "https://github.com/ziglibs/diffz/archive/2fd03fc72760a700e41f30f2b180f26e11c3365b.tar.gz" + }, + "original": { + "type": "tarball", + "url": "https://github.com/ziglibs/diffz/archive/2fd03fc72760a700e41f30f2b180f26e11c3365b.tar.gz" + } + }, "flake-compat": { "flake": false, "locked": { @@ -16,6 +40,22 @@ "type": "github" } }, + "flake-compat_2": { + "flake": false, + "locked": { + "lastModified": 1673956053, + "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, "flake-utils": { "locked": { "lastModified": 1659877975, @@ -31,46 +71,194 @@ "type": "github" } }, - "nixpkgs": { + "flake-utils_2": { + "inputs": { + "systems": "systems" + }, "locked": { - "lastModified": 1661151577, - "narHash": "sha256-++S0TuJtuz9IpqP8rKktWyHZKpgdyrzDFUXVY07MTRI=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "54060e816971276da05970a983487a25810c38a7", + "lastModified": 1685518550, + "narHash": "sha256-o2d0KcvaXzTrPRIo0kOLV0/QXHhDQ5DTi+OxcjO8xqY=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "a1720a10a6cfe8234c0e93907ffe81be440f4cef", "type": "github" }, "original": { - "owner": "NixOS", - "ref": "nixpkgs-unstable", - "repo": "nixpkgs", + "owner": "numtide", + "repo": "flake-utils", "type": "github" } }, + "flake-utils_3": { + "locked": { + "lastModified": 1659877975, + "narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "gitignore": { + "inputs": { + "nixpkgs": [ + "zls", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1660459072, + "narHash": "sha256-8DFJjXG8zqoONA1vXtgeKXy68KdJL5UaXR8NtVMUbx8=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "a20de23b925fd8264fd7fad6454652e142fd7f73", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, + "known_folders": { + "flake": false, + "locked": { + "narHash": "sha256-hgzm6HrtusbhmWEq6moKYGf5sWc52UwyHTjAIjr6EEQ=", + "type": "tarball", + "url": "https://github.com/ziglibs/known-folders/archive/d13ba6137084e55f873f6afb67447fe8906cc951.tar.gz" + }, + "original": { + "type": "tarball", + "url": "https://github.com/ziglibs/known-folders/archive/d13ba6137084e55f873f6afb67447fe8906cc951.tar.gz" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1687502512, + "narHash": "sha256-dBL/01TayOSZYxtY4cMXuNCBk8UMLoqRZA+94xiFpJA=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "3ae20aa58a6c0d1ca95c9b11f59a2d12eebc511f", + "type": "github" + }, + "original": { + "id": "nixpkgs", + "ref": "nixos-unstable", + "type": "indirect" + } + }, "root": { "inputs": { - "zig": "zig" + "nixpkgs": "nixpkgs", + "zig": "zig", + "zls": "zls" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "tres": { + "flake": false, + "locked": { + "narHash": "sha256-yhAH7npmBmlOkoHAitbDEv9CUx3zOc2GyErR8WPQCYQ=", + "type": "tarball", + "url": "https://github.com/ziglibs/tres/archive/220d01f3931595e3a2e2a6a0693363c0bfaf47e9.tar.gz" + }, + "original": { + "type": "tarball", + "url": "https://github.com/ziglibs/tres/archive/220d01f3931595e3a2e2a6a0693363c0bfaf47e9.tar.gz" } }, "zig": { "inputs": { "flake-compat": "flake-compat", "flake-utils": "flake-utils", - "nixpkgs": "nixpkgs" + "nixpkgs": [ + "nixpkgs" + ] }, "locked": { - "lastModified": 1684670828, - "narHash": "sha256-e3HSmT9ufQ0WZYh6hOPx87dg/4jjLoJLl6zAEzev07E=", + "lastModified": 1687653075, + "narHash": "sha256-2PH/eKQ78Oe8yspR11iIhB1mAUqDZ+xlvC5Z5lRvKyY=", "owner": "mitchellh", "repo": "zig-overlay", - "rev": "22a39bd5ce8f8b0fdec3049ca5bfb54c748380bd", + "rev": "2adb751d4e9d6ab472c44e5fe8bde98e51fec0a1", "type": "github" }, "original": { "owner": "mitchellh", + "ref": "main", "repo": "zig-overlay", "type": "github" } + }, + "zig-overlay": { + "inputs": { + "flake-compat": "flake-compat_2", + "flake-utils": "flake-utils_3", + "nixpkgs": [ + "zls", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1687653075, + "narHash": "sha256-2PH/eKQ78Oe8yspR11iIhB1mAUqDZ+xlvC5Z5lRvKyY=", + "owner": "mitchellh", + "repo": "zig-overlay", + "rev": "2adb751d4e9d6ab472c44e5fe8bde98e51fec0a1", + "type": "github" + }, + "original": { + "owner": "mitchellh", + "ref": "main", + "repo": "zig-overlay", + "type": "github" + } + }, + "zls": { + "inputs": { + "binned_allocator": "binned_allocator", + "diffz": "diffz", + "flake-utils": "flake-utils_2", + "gitignore": "gitignore", + "known_folders": "known_folders", + "nixpkgs": [ + "nixpkgs" + ], + "tres": "tres", + "zig-overlay": "zig-overlay" + }, + "locked": { + "lastModified": 1687692338, + "narHash": "sha256-RCf3DFTcldHwwtlcwzTVUU9Ej8alAGqhPvMXwrjYvjg=", + "ref": "refs/heads/master", + "rev": "c32e04ad38305af716f35c2687811707c0626854", + "revCount": 1722, + "type": "git", + "url": "https://git.andr3h3nriqu3s.com/andr3/zls" + }, + "original": { + "type": "git", + "url": "https://git.andr3h3nriqu3s.com/andr3/zls" + } } }, "root": "root", diff --git a/flake.nix b/flake.nix index 1727211..989c2b4 100644 --- a/flake.nix +++ b/flake.nix @@ -1,9 +1,18 @@ { - description = "A very basic flake"; + description = "A very basic flake"; - inputs.zig.url = "github:mitchellh/zig-overlay"; + inputs = { + nixpkgs.url ="nixpkgs/nixos-unstable"; - outputs = { self, zig, ... }: { - packages.x86_64-linux.zig = zig.packages.x86_64-linux.master; - }; + zig.url = "github:mitchellh/zig-overlay/main"; + zig.inputs.nixpkgs.follows = "nixpkgs"; + + zls.url = "git+https://git.andr3h3nriqu3s.com/andr3/zls"; + zls.inputs.nixpkgs.follows = "nixpkgs"; + }; + + outputs = { self, zig, zls, ... }: { + packages.x86_64-linux.zig = zig.packages.x86_64-linux.master; + packages.x86_64-linux.zls = zls.packages.x86_64-linux.default; + }; } diff --git a/src/HuffmanGraph.zig b/src/HuffmanGraph.zig new file mode 100644 index 0000000..e550da5 --- /dev/null +++ b/src/HuffmanGraph.zig @@ -0,0 +1,332 @@ +const std = @import("std"); +const mem = std.mem; +const utils = @import("utils.zig"); +const walkers = @import("walker.zig"); +const BitWalkerUint = walkers.BitWalkerUint; +const BitWalker = walkers.BitWalker; + +pub fn HuffmanGraph(comptime valueType: type) type { + return struct { + const Node = struct { + const NodeSelf = @This(); + + left: ?*NodeSelf, + right: ?*NodeSelf, + value: ?valueType, + + allocator: mem.Allocator, + + fn init(allocator: mem.Allocator) !*NodeSelf { + var node = try allocator.create(NodeSelf); + node.left = null; + node.right = null; + node.value = null; + node.allocator = allocator; + + return node; + } + + fn deinit(self: *NodeSelf) void { + if (self.left) |left| { + left.deinit(); + } + if (self.right) |right| { + right.deinit(); + } + self.allocator.destroy(self); + } + + fn print(self: *NodeSelf, curDepth: usize, targetDepth: usize) void { + if (curDepth != targetDepth) { + if (self.left) |l| { + l.print(curDepth + 1, targetDepth); + } else { + utils.printf(" , ", .{}); + } + if (self.right) |r| { + r.print(curDepth + 1, targetDepth); + } else { + utils.printf(" . ", .{}); + } + return; + } + + if (self.value) |v| { + utils.printf(" {any} ", .{v}); + } else { + utils.printf(" _ ", .{}); + } + } + + fn depth(self: *NodeSelf) usize { + var d: usize = 0; + if (self.right) |r| { + d = r.depth(); + } + if (self.left) |l| { + d = std.math.max(d, l.depth()); + } + return d + 1; + } + + fn valid(self: *NodeSelf) bool { + var has_child = self.right == null and self.left == null; + if (self.value != null) + return has_child; + + if (has_child) + return false; + + if (self.right) |r| + if (!r.valid()) + return false; + + if (self.left) |l| + if (!l.valid()) + return false; + + return true; + } + }; + + root: *Node, + allocator: mem.Allocator, + stored_depth: ?usize = null, + + iterNode: *Node, + + const Self = @This(); + + fn init(allocator: mem.Allocator) !Self { + var root = try Node.init(allocator); + return Self{ + .root = root, + .iterNode = root, + .allocator = allocator, + }; + } + + pub fn print(self: *Self) void { + var d = self.depth() + 1; + for (0..d) |i| { + self.root.print(0, i); + utils.printf("\n", .{}); + } + } + + fn addValue(self: *Self, code: u64, size: usize, value: valueType) !void { + var walker = try BitWalkerUint(u64).init(code, size, true); + + var curNode: *Node = self.root; + + while (walker.walkBit()) |bit| { + if (bit == 1) { + if (curNode.left) |nextNode| { + curNode = nextNode; + } else { + curNode.left = try Node.init(self.allocator); + curNode = curNode.left.?; + } + } else { + if (curNode.right) |nextNode| { + curNode = nextNode; + } else { + curNode.right = try Node.init(self.allocator); + curNode = curNode.right.?; + } + } + } + + curNode.value = value; + self.stored_depth = null; + } + + fn depth(self: *Self) usize { + if (self.stored_depth) |d| { + return d; + } + + self.stored_depth = self.root.depth() - 1; + return self.stored_depth.?; + } + + fn iter(self: *Self, bit: u1) anyerror!?valueType { + var node = self.iterNode; + + var nextNode: ?*Node = null; + + if (bit == 1) { + nextNode = node.left; + } else { + nextNode = node.right; + } + + if (nextNode) |new_node| { + if (new_node.value) |value| { + self.iterNode = self.root; + return value; + } else { + self.iterNode = new_node; + return null; + } + } + + return error.InvalidBitSequence; + } + + pub fn nextBitW(self: *Self, bitw: *BitWalker) !valueType { + while (true) { + if (try self.iter(try bitw.bitWalk())) |value| + return value; + } + } + + fn valid(self: *Self) bool { + return self.root.valid(); + } + + pub fn deinit(self: *Self) void { + self.root.deinit(); + } + }; +} + +pub fn create_dynamic_graph(walker: *BitWalker, len_to_read: usize, allocator: mem.Allocator) !HuffmanGraph(u64) { + const list: [19]u8 = .{ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; + + const lens: []u8 = try allocator.alloc(u8, 19); + defer allocator.free(lens); + mem.set(u8, lens, 0); + + for (0..len_to_read) |i| { + lens[list[i]] = @intCast(u8, try walker.walk(3)); + } + + var builder = try GraphBuilder(u64).init(allocator, 19, 0, 8); + defer builder.deinit(); + + while (!builder.done()) { + builder.addValue(builder.i, lens[builder.i]); + } + + return try builder.build(); +} + +pub fn GraphBuilder(comptime T: type) type { + return struct { + const Self = @This(); + + allocator: mem.Allocator, + values: []T, + lens: []usize, + bl_count: []usize, + size: usize, + max: usize = 0, + i: usize = 0, + + pub fn init(allocator: mem.Allocator, size: usize, default: T, maxlen: usize) !Self { + var values = try allocator.alloc(T, size); + mem.set(T, values, default); + + var lens = try allocator.alloc(usize, size); + mem.set(usize, lens, 0); + + var bl_count = try allocator.alloc(usize, maxlen); + mem.set(usize, bl_count, 0); + + return Self{ + .allocator = allocator, + .bl_count = bl_count, + .size = size, + .values = values, + .lens = lens, + }; + } + + pub fn skip(self: *Self, len: usize) void { + self.i += len; + } + + pub fn done(self: *Self) bool { + return self.i >= self.size; + } + + pub fn addValue(self: *Self, value: T, len: usize) void { + if (len > self.max) { + self.max = len; + } + self.values[self.i] = value; + self.lens[self.i] = len; + self.bl_count[len] += 1; + self.i += 1; + } + + pub fn build(self: *Self) !HuffmanGraph(T) { + self.bl_count[0] = 0; + + var next_code = try self.allocator.alloc(usize, self.max + 1); + defer self.allocator.free(next_code); + std.mem.set(usize, next_code, 0); + + var code: usize = 0; + for (1..(self.max + 1)) |bits| { + code = @shlExact(code + self.bl_count[bits - 1], 1); + next_code[bits] = code; + } + + var codes = try self.allocator.alloc(u64, self.size); + defer self.allocator.free(codes); + std.mem.set(usize, codes, 0); + + for (0..self.size) |n| { + var len = self.lens[n]; + if (len != 0) { + codes[n] = next_code[len]; + next_code[len] += 1; + } + } + + var graph = try HuffmanGraph(T).init(self.allocator); + errdefer graph.deinit(); + + for (0..self.size) |i| { + if (self.lens[i] == 0) + continue; + try graph.addValue(codes[i], self.lens[i], self.values[i]); + } + + if (!graph.valid()) + return error.graph_not_valid; + + return graph; + } + + pub fn buildWithDynamic(self: *Self, bitw: *BitWalker, graph: *HuffmanGraph(u64)) !HuffmanGraph(T) { + while (!self.done()) { + var v = try graph.nextBitW(bitw); + + if (v == 16) { + const len = try bitw.walk(2) + 3; + const last = self.lens[self.i - 1]; + for (0..len) |_| { + self.addValue(self.i, last); + } + } else if (v == 17) { + self.skip(try bitw.walk(3) + 3); + } else if (v == 18) { + self.skip(try bitw.walk(7) + 11); + } else { + self.addValue(self.i, v); + } + } + + return try self.build(); + } + + pub fn deinit(self: *Self) void { + self.allocator.free(self.values); + self.allocator.free(self.lens); + self.allocator.free(self.bl_count); + } + }; +} diff --git a/src/zip.zig b/src/zip.zig new file mode 100644 index 0000000..458a42c --- /dev/null +++ b/src/zip.zig @@ -0,0 +1,408 @@ +const std = @import("std"); +const mem = std.mem; + +const utils = @import("utils.zig"); +const print = utils.print; + +const walkers = @import("walker.zig"); +const BitWalker = walkers.BitWalker; +const BitWalkerUint = walkers.BitWalkerUint; + +const huffmanstuff = @import("HuffmanGraph.zig"); +const HuffmanGraph = huffmanstuff.HuffmanGraph; +const GraphBuilder = huffmanstuff.GraphBuilder; +const create_dynamic_graph = huffmanstuff.create_dynamic_graph; + +fn get_len_value(bitw: *BitWalker, len_code: usize) !usize { + var extra_bits: usize = switch (len_code) { + 257...264 => 0, + 265...268 => 1, + 269...272 => 2, + 273...276 => 3, + 277...280 => 4, + 281...284 => 5, + 285 => 0, + else => unreachable, + }; + + var to_add = try bitw.walk(extra_bits); + + var base_value: usize = switch (len_code) { + 257 => 3, + 258 => 4, + 259 => 5, + 260 => 6, + 261 => 7, + 262 => 8, + 263 => 9, + 264 => 10, + 265 => 11, + 266 => 13, + 267 => 15, + 268 => 17, + 269 => 19, + 270 => 23, + 271 => 27, + 272 => 31, + 273 => 35, + 274 => 43, + 275 => 51, + 276 => 59, + 277 => 67, + 278 => 83, + 279 => 99, + 280 => 115, + 281 => 131, + 282 => 163, + 283 => 195, + 284 => 227, + 285 => 258, + else => unreachable, + }; + + return base_value + to_add; +} + +fn get_dist_value(bitw: *BitWalker, dist_graph: *HuffmanGraph(u64)) !usize { + var dist = try dist_graph.nextBitW(bitw); + + var extra_bits: usize = switch (dist) { + 0...3 => 0, + 4...29 => (dist / 2) - 1, + else => unreachable, + }; + + var to_add = try bitw.walk(extra_bits); + + var base_value: usize = switch (dist) { + 0...3 => dist + 1, + // import math + // def d(dist): + // return 2**math.floor(dist/2) + (2**(math.floor(dist / 2) - 1) * (dist % 2)) + 1 + 4...29 => std.math.pow(usize, 2, dist / 2) + (std.math.pow(usize, 2, (dist / 2) - 1) * (dist % 2)) + 1, + else => unreachable, + }; + + return base_value + to_add; +} + +const BlockData = struct { + const Self = @This(); + + bitw: *BitWalker, + allocator: mem.Allocator, + + last: bool, + blockType: u8, + + literal_graph: ?HuffmanGraph(u64) = null, + dist_graph: ?HuffmanGraph(u64) = null, + output: *[]u8, + + allOutput: *[]u8, + + fn init(allocator: mem.Allocator, bitw: *BitWalker, output: *[]u8, allOutput: *[]u8) !Self { + return Self{ + .bitw = bitw, + .allocator = allocator, + .last = try bitw.walk(1) == 1, + .blockType = @intCast(u8, try bitw.walk(2)), + + .output = output, + .allOutput = allOutput, + }; + } + + fn uncompress(self: *Self, start_place: usize) !usize { + return switch (self.blockType) { + 0 => self.not_compressed(start_place), + 1 => self.fixed(start_place), + 2 => self.dynamic_huffman(start_place), + 3 => error.block_type_error, + else => unreachable, + }; + } + + fn not_compressed(self: *Self, start_value: usize) !usize { + var bitw = self.bitw; + try bitw.nextByte(); + + var size = try bitw.walk(16); + var nsize = try bitw.walk(24); + _ = nsize; + + var i = start_value; + while (i < size) : (i += 1) { + self.output.ptr[i] = @intCast(u8, try bitw.walk(8)); + } + + return i; + } + + fn fixed(self: *Self, start_value: usize) !usize { + var litBuilder = try GraphBuilder(u64).init(self.allocator, 288, 0, 10); + defer litBuilder.deinit(); + + for (0..144) |i| { + litBuilder.addValue(i, 8); + } + for (144..256) |i| { + litBuilder.addValue(i, 9); + } + for (256..280) |i| { + litBuilder.addValue(i, 7); + } + for (280..288) |i| { + litBuilder.addValue(i, 8); + } + + var lit = try litBuilder.build(); + + self.literal_graph = lit; + + var distBuilder = try GraphBuilder(u64).init(self.allocator, 32, 0, 10); + defer distBuilder.deinit(); + + for (0..32) |i| { + distBuilder.addValue(i, 5); + } + + self.dist_graph = try distBuilder.build(); + + var bitw = self.bitw; + + var lastRead: u64 = 0; + var i: usize = start_value; + while (true) { + lastRead = try lit.nextBitW(bitw); + if (lastRead == 256) { + break; + } else if (lastRead > 256) { + var len = try get_len_value(bitw, lastRead); + var dist = try get_dist_value(bitw, &self.dist_graph.?); + + print("{} {}", .{ i, dist }); + + var pos: isize = @intCast(isize, i) - @intCast(isize, dist); + for (0..len) |j| { + if ((pos + @intCast(isize, j)) < 0) { + self.output.ptr[i] = self.allOutput.ptr[@intCast(usize, @intCast(isize, self.allOutput.len) - pos + @intCast(isize, j))]; + } else { + self.output.ptr[i] = self.output.ptr[@intCast(usize, pos + @intCast(isize, j))]; + } + i += 1; + } + } else if (lastRead < 256) { + self.output.ptr[i] = @intCast(u8, lastRead); + i += 1; + } else { + unreachable; + } + } + + return i; + } + + fn dynamic_huffman(self: *Self, start_place: usize) !usize { + var bitw = self.bitw; + + var number_of_literal_codes: u32 = @intCast(u32, try bitw.walk(5)) + 257; + var number_of_dist_codes = try bitw.walk(5) + 1; + var number_of_length_codes = try bitw.walk(4) + 4; + + var dynamic_graph = try create_dynamic_graph(bitw, number_of_length_codes, self.allocator); + defer dynamic_graph.deinit(); + + var builder = try GraphBuilder(u64).init(self.allocator, number_of_literal_codes, 0, 16); + // destory either the 1st or the 2nd graph + defer builder.deinit(); + + self.literal_graph = try builder.buildWithDynamic(bitw, &dynamic_graph); + var graph = self.literal_graph.?; + + // Destory the first builder + builder.deinit(); + + builder = try GraphBuilder(u64).init(self.allocator, number_of_dist_codes, 0, 16); + self.dist_graph = try builder.buildWithDynamic(bitw, &dynamic_graph); + + var lastRead: u64 = 0; + + var i: usize = start_place; + while (true) { + lastRead = try graph.nextBitW(bitw); + if (lastRead == 256) { + break; + } else if (lastRead > 256) { + var len = try get_len_value(bitw, lastRead); + var dist = try get_dist_value(bitw, &self.dist_graph.?); + + var pos: isize = @intCast(isize, i) - @intCast(isize, dist); + for (0..len) |j| { + if ((pos + @intCast(isize, j)) < 0) { + self.output.ptr[i] = self.allOutput.ptr[@intCast(usize, @intCast(isize, self.allOutput.len) - pos + @intCast(isize, j))]; + } else { + self.output.ptr[i] = self.output.ptr[@intCast(usize, pos + @intCast(isize, j))]; + } + i += 1; + } + } else if (lastRead < 256) { + self.output.ptr[i] = @intCast(u8, lastRead); + i += 1; + } else { + unreachable; + } + } + + return i; + } + + fn deinit(self: *Self) void { + if (self.literal_graph != null) { + self.literal_graph.?.deinit(); + } + if (self.dist_graph != null) { + self.dist_graph.?.deinit(); + } + } +}; + +const LOCAL_FILE_HEADER_SIGNATURE = 0x04034b50; +const ZipFileHeader = struct { + version: u16, + general: u16, + compression_method: u16, + last_mod_time: u16, + last_mod_date: u16, + crc_32: u32, + compressed_size: u32, + uncompressed_size: u32, + file_name_length: u16, + extra_field_length: u16, + + file_name: []u8, + extra_field: []u8, + compressed_content: []u8, + uncompressed_content: []u8, + decompressed: bool, + + allocator: std.mem.Allocator, + + allOutput: *[]u8, + + const Self = @This(); + + fn init(allocator: std.mem.Allocator, reader: std.fs.File.Reader, allOutput: *[]u8) !Self { + if (try reader.readInt(u32, .Big) == LOCAL_FILE_HEADER_SIGNATURE) { + return error.InvalidError; + } + + var self = Self{ + .allocator = allocator, + .version = try reader.readInt(u16, .Little), + .general = try reader.readInt(u16, .Little), + .compression_method = try reader.readInt(u16, .Little), + .last_mod_time = try reader.readInt(u16, .Little), + .last_mod_date = try reader.readInt(u16, .Little), + .crc_32 = try reader.readInt(u32, .Little), + .compressed_size = try reader.readInt(u32, .Little), + .uncompressed_size = try reader.readInt(u32, .Little), + .file_name_length = try reader.readInt(u16, .Little), + .extra_field_length = try reader.readInt(u16, .Little), + .file_name = undefined, + .extra_field = undefined, + .compressed_content = undefined, + .uncompressed_content = undefined, + .decompressed = false, + .allOutput = allOutput, + }; + + self.file_name = try allocator.alloc(u8, self.file_name_length); + self.extra_field = try allocator.alloc(u8, self.extra_field_length); + self.compressed_content = try allocator.alloc(u8, self.compressed_size); + + _ = try reader.read(self.file_name); + _ = try reader.read(self.extra_field); + _ = try reader.read(self.compressed_content); + + return self; + } + + fn extract(self: *Self) !void { + // already decompressed + if (self.decompressed) return; + + // already decompressed + if (self.compression_method == 0) { + self.uncompressed_content = try self.allocator.alloc(u8, self.uncompressed_size); + mem.copy(u8, self.uncompressed_content, self.compressed_content); + return; + } + + if (self.compression_method != 8) { + return error.unsuported_compression_method; + } + + self.uncompressed_content = try self.allocator.alloc(u8, self.uncompressed_size); + errdefer self.allocator.free(self.uncompressed_content); + mem.set(u8, self.uncompressed_content, 0); + + var bitw = try BitWalker.init(&self.compressed_content); + + var is_last = false; + var output_place: usize = 0; + + while (!is_last) { + var block = try BlockData.init(self.allocator, &bitw, &self.uncompressed_content, self.allOutput); + defer block.deinit(); + + is_last = block.last; + + output_place = try block.uncompress(output_place); + } + + self.decompressed = true; + } + + pub fn deinit(self: *Self) void { + self.allocator.free(self.file_name); + self.allocator.free(self.extra_field); + self.allocator.free(self.compressed_content); + if (self.decompressed) { + self.allocator.free(self.uncompressed_content); + } + } +}; + +pub const ZipFile = struct { + const Self = @This(); + + allocator: std.mem.Allocator, + reader: std.fs.File.Reader, + + allOutput: []u8, + + pub fn init(allocator: std.mem.Allocator, reader: std.fs.File.Reader) !Self { + return Self{ + .allocator = allocator, + .reader = reader, + .allOutput = try allocator.alloc(u8, 0), + }; + } + + pub fn readFile(self: *Self) !ZipFileHeader { + var zip_header = try ZipFileHeader.init(self.allocator, self.reader, &self.allOutput); + errdefer zip_header.deinit(); + try zip_header.extract(); + + var new_all = try mem.concat(self.allocator, u8, &.{ self.allOutput, zip_header.uncompressed_content }); + self.allocator.free(self.allOutput); + self.allOutput = new_all; + + return zip_header; + } + + pub fn deinit(self: *Self) void { + self.allocator.free(self.allOutput); + } +};