mirror of
https://github.com/ziglang/zig.git
synced 2025-12-06 14:23:09 +00:00
146 lines
4.6 KiB
Zig
146 lines
4.6 KiB
Zig
const std = @import("std");
|
|
const block = @import("xz/block.zig");
|
|
const Allocator = std.mem.Allocator;
|
|
const Crc32 = std.hash.Crc32;
|
|
|
|
pub const Check = enum(u4) {
|
|
none = 0x00,
|
|
crc32 = 0x01,
|
|
crc64 = 0x04,
|
|
sha256 = 0x0A,
|
|
_,
|
|
};
|
|
|
|
fn readStreamFlags(reader: anytype, check: *Check) !void {
|
|
var bit_reader = std.io.bitReader(.little, reader);
|
|
|
|
const reserved1 = try bit_reader.readBitsNoEof(u8, 8);
|
|
if (reserved1 != 0)
|
|
return error.CorruptInput;
|
|
|
|
check.* = @as(Check, @enumFromInt(try bit_reader.readBitsNoEof(u4, 4)));
|
|
|
|
const reserved2 = try bit_reader.readBitsNoEof(u4, 4);
|
|
if (reserved2 != 0)
|
|
return error.CorruptInput;
|
|
}
|
|
|
|
pub fn decompress(allocator: Allocator, reader: anytype) !Decompress(@TypeOf(reader)) {
|
|
return Decompress(@TypeOf(reader)).init(allocator, reader);
|
|
}
|
|
|
|
pub fn Decompress(comptime ReaderType: type) type {
|
|
return struct {
|
|
const Self = @This();
|
|
|
|
pub const Error = ReaderType.Error || block.Decoder(ReaderType).Error;
|
|
pub const Reader = std.io.GenericReader(*Self, Error, read);
|
|
|
|
allocator: Allocator,
|
|
block_decoder: block.Decoder(ReaderType),
|
|
in_reader: ReaderType,
|
|
|
|
fn init(allocator: Allocator, source: ReaderType) !Self {
|
|
const magic = try source.readBytesNoEof(6);
|
|
if (!std.mem.eql(u8, &magic, &.{ 0xFD, '7', 'z', 'X', 'Z', 0x00 }))
|
|
return error.BadHeader;
|
|
|
|
var check: Check = undefined;
|
|
const hash_a = blk: {
|
|
var hasher = std.compress.hashedReader(source, Crc32.init());
|
|
try readStreamFlags(hasher.reader(), &check);
|
|
break :blk hasher.hasher.final();
|
|
};
|
|
|
|
const hash_b = try source.readInt(u32, .little);
|
|
if (hash_a != hash_b)
|
|
return error.WrongChecksum;
|
|
|
|
return Self{
|
|
.allocator = allocator,
|
|
.block_decoder = try block.decoder(allocator, source, check),
|
|
.in_reader = source,
|
|
};
|
|
}
|
|
|
|
pub fn deinit(self: *Self) void {
|
|
self.block_decoder.deinit();
|
|
}
|
|
|
|
pub fn reader(self: *Self) Reader {
|
|
return .{ .context = self };
|
|
}
|
|
|
|
pub fn read(self: *Self, buffer: []u8) Error!usize {
|
|
if (buffer.len == 0)
|
|
return 0;
|
|
|
|
const r = try self.block_decoder.read(buffer);
|
|
if (r != 0)
|
|
return r;
|
|
|
|
const index_size = blk: {
|
|
var hasher = std.compress.hashedReader(self.in_reader, Crc32.init());
|
|
hasher.hasher.update(&[1]u8{0x00});
|
|
|
|
var counter = std.io.countingReader(hasher.reader());
|
|
counter.bytes_read += 1;
|
|
|
|
const counting_reader = counter.reader();
|
|
|
|
const record_count = try std.leb.readUleb128(u64, counting_reader);
|
|
if (record_count != self.block_decoder.block_count)
|
|
return error.CorruptInput;
|
|
|
|
var i: usize = 0;
|
|
while (i < record_count) : (i += 1) {
|
|
// TODO: validate records
|
|
_ = try std.leb.readUleb128(u64, counting_reader);
|
|
_ = try std.leb.readUleb128(u64, counting_reader);
|
|
}
|
|
|
|
while (counter.bytes_read % 4 != 0) {
|
|
if (try counting_reader.readByte() != 0)
|
|
return error.CorruptInput;
|
|
}
|
|
|
|
const hash_a = hasher.hasher.final();
|
|
const hash_b = try counting_reader.readInt(u32, .little);
|
|
if (hash_a != hash_b)
|
|
return error.WrongChecksum;
|
|
|
|
break :blk counter.bytes_read;
|
|
};
|
|
|
|
const hash_a = try self.in_reader.readInt(u32, .little);
|
|
|
|
const hash_b = blk: {
|
|
var hasher = std.compress.hashedReader(self.in_reader, Crc32.init());
|
|
const hashed_reader = hasher.reader();
|
|
|
|
const backward_size = (@as(u64, try hashed_reader.readInt(u32, .little)) + 1) * 4;
|
|
if (backward_size != index_size)
|
|
return error.CorruptInput;
|
|
|
|
var check: Check = undefined;
|
|
try readStreamFlags(hashed_reader, &check);
|
|
|
|
break :blk hasher.hasher.final();
|
|
};
|
|
|
|
if (hash_a != hash_b)
|
|
return error.WrongChecksum;
|
|
|
|
const magic = try self.in_reader.readBytesNoEof(2);
|
|
if (!std.mem.eql(u8, &magic, &.{ 'Y', 'Z' }))
|
|
return error.CorruptInput;
|
|
|
|
return 0;
|
|
}
|
|
};
|
|
}
|
|
|
|
test {
|
|
_ = @import("xz/test.zig");
|
|
}
|