| Check | 
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;
}
 | 
| Decompress() | 
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.Reader(*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,
            };
        }
 | 
| read() | 
        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");
}
 |