Zchunk: C++98/STL parsing library

File extension

["zck", "zhr"]

KS implementation details

License: CC0-1.0
Minimal Kaitai Struct required: 0.10

References

This page hosts a formal specification of Zchunk using Kaitai Struct. This specification can be automatically translated into a variety of programming languages to get a parsing library.

Usage

Runtime library

All C++98/STL code generated by Kaitai Struct depends on the Kaitai Struct runtime library for C++/STL. You must add this dependency to your project before you can parse or serialize any data.

For C++, the easiest way is to clone the runtime library sources and build them along with your project.

Code

Using Kaitai Struct in C++/STL usually consists of 3 steps.

  1. We need to create an STL input stream (std::istream). One can open local file for that, or use existing std::string or char* buffer.
    #include <fstream>
    
    std::ifstream is("path/to/local/file.zck", std::ifstream::binary);
    
    #include <sstream>
    
    std::istringstream is(str);
    
    #include <sstream>
    
    const char buf[] = { ... };
    std::string str(buf, sizeof buf);
    std::istringstream is(str);
    
  2. We need to wrap our input stream into Kaitai stream:
    #include "kaitai/kaitaistream.h"
    
    kaitai::kstream ks(&is);
    
  3. And finally, we can invoke the parsing:
    zchunk_t data(&ks);
    

After that, one can get various attributes from the structure by invoking getter methods like:

data.dict() // => Custom dictionary used when compressing each chunk. It's compressed itself
without a dictionary.

The official zchunk specification calls this section "Compressed Dict".
It's also called a "dictionary chunk". `zck_read_header -c` presents it as
"chunk 0" (which is always shown in the chunk table, but can have size 0
if the dictionary is not in use).

C++98/STL source code to parse Zchunk

zchunk.h

#ifndef ZCHUNK_H_
#define ZCHUNK_H_

// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild

class zchunk_t;

#include "kaitai/kaitaistruct.h"
#include <stdint.h>
#include <set>
#include <vector>

#if KAITAI_STRUCT_VERSION < 11000L
#error "Incompatible Kaitai Struct C++/STL API: version 0.11 or later is required"
#endif

/**
 * \sa https://github.com/zchunk/zchunk/blob/99e51afa38c723e7c25834c2c3b305d20ef55d04/zchunk_format.txt Source
 */

class zchunk_t : public kaitai::kstruct {

public:
    class checksum_type_t;
    class chunk_t;
    class compressed_integer_t;
    class header_lead_t;
    class header_without_lead_t;
    class index_t;
    class optional_element_t;
    class preface_t;

    enum checksum_types_t {
        CHECKSUM_TYPES_SHA1 = 0,
        CHECKSUM_TYPES_SHA256 = 1,
        CHECKSUM_TYPES_SHA512 = 2,
        CHECKSUM_TYPES_SHA512_128 = 3
    };
    static bool _is_defined_checksum_types_t(checksum_types_t v);

private:
    static const std::set<checksum_types_t> _values_checksum_types_t;
    static std::set<checksum_types_t> _build_values_checksum_types_t();

public:

    enum compression_types_t {
        COMPRESSION_TYPES_NONE = 0,
        COMPRESSION_TYPES_ZSTD = 2
    };
    static bool _is_defined_compression_types_t(compression_types_t v);

private:
    static const std::set<compression_types_t> _values_compression_types_t;
    static std::set<compression_types_t> _build_values_compression_types_t();

public:

    zchunk_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = 0, zchunk_t* p__root = 0);

private:
    void _read();
    void _clean_up();

public:
    ~zchunk_t();

    class checksum_type_t : public kaitai::kstruct {

    public:

        checksum_type_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = 0, zchunk_t* p__root = 0);

    private:
        void _read();
        void _clean_up();

    public:
        ~checksum_type_t();

    private:
        bool f_len_checksum;
        int8_t m_len_checksum;

    public:
        int8_t len_checksum();

    private:
        bool f_value;
        checksum_types_t m_value;

    public:
        checksum_types_t value();

    private:
        compressed_integer_t* m_raw;
        zchunk_t* m__root;
        kaitai::kstruct* m__parent;

    public:

        /**
         * Raw integer, don't read this field - access `value` instead.
         */
        compressed_integer_t* raw() const { return m_raw; }
        zchunk_t* _root() const { return m__root; }
        kaitai::kstruct* _parent() const { return m__parent; }
    };

    class chunk_t : public kaitai::kstruct {

    public:

        chunk_t(uint32_t p_len_checksum, bool p_has_data_streams, bool p_has_uncompressed_source, kaitai::kstream* p__io, zchunk_t::index_t* p__parent = 0, zchunk_t* p__root = 0);

    private:
        void _read();
        void _clean_up();

    public:
        ~chunk_t();

    private:
        compressed_integer_t* m_chunk_stream;
        bool n_chunk_stream;

    public:
        bool _is_null_chunk_stream() { chunk_stream(); return n_chunk_stream; };

    private:
        std::string m_chunk_checksum;
        std::string m_uncompressed_chunk_checksum;
        bool n_uncompressed_chunk_checksum;

    public:
        bool _is_null_uncompressed_chunk_checksum() { uncompressed_chunk_checksum(); return n_uncompressed_chunk_checksum; };

    private:
        compressed_integer_t* m_len_chunk;
        compressed_integer_t* m_len_uncompressed_chunk;
        uint32_t m_len_checksum;
        bool m_has_data_streams;
        bool m_has_uncompressed_source;
        zchunk_t* m__root;
        zchunk_t::index_t* m__parent;

    public:
        compressed_integer_t* chunk_stream() const { return m_chunk_stream; }
        std::string chunk_checksum() const { return m_chunk_checksum; }

        /**
         * Checksum of the uncompressed chunk. Used to detect whether a chunk
         * from an uncompressed source is identical to the compressed chunk.
         */
        std::string uncompressed_chunk_checksum() const { return m_uncompressed_chunk_checksum; }
        compressed_integer_t* len_chunk() const { return m_len_chunk; }
        compressed_integer_t* len_uncompressed_chunk() const { return m_len_uncompressed_chunk; }
        uint32_t len_checksum() const { return m_len_checksum; }
        bool has_data_streams() const { return m_has_data_streams; }
        bool has_uncompressed_source() const { return m_has_uncompressed_source; }
        zchunk_t* _root() const { return m__root; }
        zchunk_t::index_t* _parent() const { return m__parent; }
    };

    /**
     * Like `/common/vlq_base128_le` (LEB128), but the logic of the
     * "continuation" flag in the most significant bit is inverted, so instead of
     * `has_next`, it is called `is_last` (if the highest bit is set to zero, it
     * means "continue", whereas in standard LEB128, the highest bit set to
     * **one** means "continue"). Therefore, we cannot simply import
     * `/common/vlq_base128_le` and use it, because it is incompatible.
     */

    class compressed_integer_t : public kaitai::kstruct {

    public:
        class group_t;

        compressed_integer_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = 0, zchunk_t* p__root = 0);

    private:
        void _read();
        void _clean_up();

    public:
        ~compressed_integer_t();

        /**
         * One byte group, clearly divided into 7-bit "value" chunk and 1-bit "continuation" flag.
         */

        class group_t : public kaitai::kstruct {

        public:

            group_t(int32_t p_idx, kaitai::kstream* p__io, zchunk_t::compressed_integer_t* p__parent = 0, zchunk_t* p__root = 0);

        private:
            void _read();
            void _clean_up();

        public:
            ~group_t();

        private:
            bool m_is_last;
            uint64_t m_value;
            int32_t m_idx;
            zchunk_t* m__root;
            zchunk_t::compressed_integer_t* m__parent;

        public:

            /**
             * If `true`, then this is the last byte of the compressed integer.
             * 
             * Since this implementation only supports serialized values up to 10
             * bytes, this must be `true` in the 10th group (`groups[9]`).
             */
            bool is_last() const { return m_is_last; }

            /**
             * The 7-bit (base128) numeric value chunk of this group
             * 
             * Since this implementation only supports integer values up to 64 bits,
             * the `value` in the 10th group (`groups[9]`) can only be `0` or `1`
             * (otherwise the width of the represented value would be 65 bits or
             * more, which is not supported).
             */
            uint64_t value() const { return m_value; }
            int32_t idx() const { return m_idx; }
            zchunk_t* _root() const { return m__root; }
            zchunk_t::compressed_integer_t* _parent() const { return m__parent; }
        };

    private:
        bool f_len;
        int32_t m_len;

    public:
        int32_t len();

    private:
        bool f_value;
        uint64_t m_value;

    public:

        /**
         * Resulting unsigned value as normal integer
         */
        uint64_t value();

    private:
        std::vector<group_t*>* m_groups;
        zchunk_t* m__root;
        kaitai::kstruct* m__parent;

    public:
        std::vector<group_t*>* groups() const { return m_groups; }
        zchunk_t* _root() const { return m__root; }
        kaitai::kstruct* _parent() const { return m__parent; }
    };

    class header_lead_t : public kaitai::kstruct {

    public:

        header_lead_t(kaitai::kstream* p__io, zchunk_t* p__parent = 0, zchunk_t* p__root = 0);

    private:
        void _read();
        void _clean_up();

    public:
        ~header_lead_t();

    private:
        bool f_is_detached_header;
        bool m_is_detached_header;

    public:

        /**
         * Determines whether this file is a zchunk detached header (`.zhr`). If
         * not, it is a complete zchunk file (`.zck`).
         */
        bool is_detached_header();

    private:
        std::string m_magic;
        checksum_type_t* m_overall_checksum_type;
        compressed_integer_t* m_len_header_rest;
        std::string m_header_checksum;
        zchunk_t* m__root;
        zchunk_t* m__parent;

    public:

        /**
         * There are two valid magic numbers for zchunk files:
         * 
         * * `'\0ZCK1'` identifies a zchunk version 1 file (`.zck`)
         * * `'\0ZHR1'` identifies a zchunk version 1 detached header file (`.zhr`)
         */
        std::string magic() const { return m_magic; }

        /**
         * Type of the checksum used for `header_checksum` and
         * `_root.header_rest.preface.data_checksum`.
         */
        checksum_type_t* overall_checksum_type() const { return m_overall_checksum_type; }

        /**
         * Size of the header, not including the lead
         */
        compressed_integer_t* len_header_rest() const { return m_len_header_rest; }

        /**
         * Checksum of the entire header, which consists of `_root.lead` and
         * `_root.header_rest` (i.e. everything from the beginning of the file to
         * the end of `_root.header_rest`), not including the `header_checksum`
         * field itself (i.e. the input for the checksum algorithm is a
         * concatenation of the bytes preceding the `header_checksum` field with
         * the bytes following it).
         * 
         * For detached headers, the checksum is calculated as if the `magic`
         * field were set to `'\0ZCK1'`, so that it matches the checksum in the
         * full zchunk file.
         */
        std::string header_checksum() const { return m_header_checksum; }
        zchunk_t* _root() const { return m__root; }
        zchunk_t* _parent() const { return m__parent; }
    };

    class header_without_lead_t : public kaitai::kstruct {

    public:

        header_without_lead_t(kaitai::kstream* p__io, zchunk_t* p__parent = 0, zchunk_t* p__root = 0);

    private:
        void _read();
        void _clean_up();

    public:
        ~header_without_lead_t();

    private:
        preface_t* m_preface;
        compressed_integer_t* m_len_index;
        index_t* m_index;
        compressed_integer_t* m_num_signatures;
        zchunk_t* m__root;
        zchunk_t* m__parent;
        std::string m__raw_index;
        kaitai::kstream* m__io__raw_index;

    public:
        preface_t* preface() const { return m_preface; }
        compressed_integer_t* len_index() const { return m_len_index; }
        index_t* index() const { return m_index; }

        /**
         * Must be 0. The reference implementation also rejects any file with a
         * non-zero "Signature count", throwing a fatal error stating "Signatures
         * aren't supported yet" - see
         * [`src/lib/header.c:259-264`](https://github.com/zchunk/zchunk/blob/99e51afa38c723e7c25834c2c3b305d20ef55d04/src/lib/header.c#L259-L264).
         * 
         * Although the structure of signatures is defined [in the official
         * textual
         * specification](https://github.com/zchunk/zchunk/blob/99e51afa38c723e7c25834c2c3b305d20ef55d04/zchunk_format.txt#L219-L252),
         * no signature types are defined, and as of this writing no publicly
         * known implementation generates or interprets these signatures.
         * Therefore, we've decided not to implement them here either.
         * 
         * For more details, see
         * <https://github.com/kaitai-io/kaitai_struct_formats/pull/539#discussion_r3713109887>.
         */
        compressed_integer_t* num_signatures() const { return m_num_signatures; }
        zchunk_t* _root() const { return m__root; }
        zchunk_t* _parent() const { return m__parent; }
        std::string _raw_index() const { return m__raw_index; }
        kaitai::kstream* _io__raw_index() const { return m__io__raw_index; }
    };

    class index_t : public kaitai::kstruct {

    public:

        index_t(kaitai::kstream* p__io, zchunk_t::header_without_lead_t* p__parent = 0, zchunk_t* p__root = 0);

    private:
        void _read();
        void _clean_up();

    public:
        ~index_t();

    private:
        bool f_num_data_chunks;
        int32_t m_num_data_chunks;

    public:

        /**
         * Number of data chunks. `num_chunks` counts the dictionary as chunk 0,
         * so it is one greater than this number.
         */
        int32_t num_data_chunks();

    private:
        checksum_type_t* m_chunk_checksum_type;
        compressed_integer_t* m_num_chunks;
        compressed_integer_t* m_dict_stream;
        bool n_dict_stream;

    public:
        bool _is_null_dict_stream() { dict_stream(); return n_dict_stream; };

    private:
        std::string m_dict_checksum;
        std::string m_uncompressed_dict_checksum;
        bool n_uncompressed_dict_checksum;

    public:
        bool _is_null_uncompressed_dict_checksum() { uncompressed_dict_checksum(); return n_uncompressed_dict_checksum; };

    private:
        compressed_integer_t* m_len_dict;
        compressed_integer_t* m_len_uncompressed_dict;
        std::vector<chunk_t*>* m_chunks_metadata;
        zchunk_t* m__root;
        zchunk_t::header_without_lead_t* m__parent;

    public:

        /**
         * Type of the checksum used for `dict_checksum` and for all
         * `chunks_metadata[...].chunk_checksum` and
         * `chunks_metadata[...].uncompressed_chunk_checksum`.
         */
        checksum_type_t* chunk_checksum_type() const { return m_chunk_checksum_type; }

        /**
         * Number of chunks, **including** the dictionary chunk.
         * 
         * Must be at least 1, because the dictionary chunk is always present,
         * even if it is empty. The reference implementation also fails when the
         * number of chunks is 0, see
         * [`src/lib/index/index_read.c:181-184`](https://github.com/zchunk/zchunk/blob/99e51afa38c723e7c25834c2c3b305d20ef55d04/src/lib/index/index_read.c#L181-L184).
         */
        compressed_integer_t* num_chunks() const { return m_num_chunks; }

        /**
         * If present, it must always be 0.
         * \sa https://github.com/zchunk/zchunk/blob/99e51afa38c723e7c25834c2c3b305d20ef55d04/zchunk_format.txt#L159-L162 Source
         */
        compressed_integer_t* dict_stream() const { return m_dict_stream; }
        std::string dict_checksum() const { return m_dict_checksum; }

        /**
         * Checksum of the uncompressed dictionary. It has no real use, as the
         * uncompressed source won't have a dictionary.
         */
        std::string uncompressed_dict_checksum() const { return m_uncompressed_dict_checksum; }
        compressed_integer_t* len_dict() const { return m_len_dict; }
        compressed_integer_t* len_uncompressed_dict() const { return m_len_uncompressed_dict; }

        /**
         * Metadata of the data chunks. The dictionary is chunk 0 and its
         * metadata is stored in the `*dict*` fields above, so there is one fewer
         * entry here than indicated by `num_chunks`.
         */
        std::vector<chunk_t*>* chunks_metadata() const { return m_chunks_metadata; }
        zchunk_t* _root() const { return m__root; }
        zchunk_t::header_without_lead_t* _parent() const { return m__parent; }
    };

    class optional_element_t : public kaitai::kstruct {

    public:

        optional_element_t(kaitai::kstream* p__io, zchunk_t::preface_t* p__parent = 0, zchunk_t* p__root = 0);

    private:
        void _read();
        void _clean_up();

    public:
        ~optional_element_t();

    private:
        compressed_integer_t* m_element_id;
        compressed_integer_t* m_len_data;
        std::string m_data;
        zchunk_t* m__root;
        zchunk_t::preface_t* m__parent;

    public:
        compressed_integer_t* element_id() const { return m_element_id; }
        compressed_integer_t* len_data() const { return m_len_data; }
        std::string data() const { return m_data; }
        zchunk_t* _root() const { return m__root; }
        zchunk_t::preface_t* _parent() const { return m__parent; }
    };

    class preface_t : public kaitai::kstruct {

    public:

        preface_t(kaitai::kstream* p__io, zchunk_t::header_without_lead_t* p__parent = 0, zchunk_t* p__root = 0);

    private:
        void _read();
        void _clean_up();

    public:
        ~preface_t();

    private:
        bool f_compression_type;
        compression_types_t m_compression_type;

    public:
        compression_types_t compression_type();

    private:
        bool f_has_data_streams;
        bool m_has_data_streams;

    public:
        bool has_data_streams();

    private:
        bool f_has_optional_elements;
        bool m_has_optional_elements;

    public:
        bool has_optional_elements();

    private:
        bool f_has_uncompressed_source;
        bool m_has_uncompressed_source;

    public:

        /**
         * The file may be applied against an uncompressed source. This adds an
         * uncompressed checksum to every index entry, including the dictionary.
         */
        bool has_uncompressed_source();

    private:
        std::string m_data_checksum;
        compressed_integer_t* m_flags;
        compressed_integer_t* m_compression_type_int;
        compressed_integer_t* m_num_optional_elements;
        bool n_num_optional_elements;

    public:
        bool _is_null_num_optional_elements() { num_optional_elements(); return n_num_optional_elements; };

    private:
        std::vector<optional_element_t*>* m_optional_elements;
        bool n_optional_elements;

    public:
        bool _is_null_optional_elements() { optional_elements(); return n_optional_elements; };

    private:
        zchunk_t* m__root;
        zchunk_t::header_without_lead_t* m__parent;

    public:

        /**
         * Total data checksum. Checksum of everything after the header,
         * including the compressed dictionary (`_root.dict`) and all compressed
         * chunks (`_root.chunks`). The type of this checksum is
         * `_root.lead.overall_checksum_type.value`.
         * 
         * If `has_uncompressed_source` is true, this checksum must not be
         * checked and should not be generated. In that case, the reference
         * implementation writes it as all zeros - see the sample file
         * [`mini-uncomp-cksums.zck`](https://github.com/kaitai-io/kaitai_struct_samples/blob/1d2fe11c971fb7e86f343b77a1ed341a0217e86a/archive/zchunk/README.md#mini-uncomp-cksumszck).
         */
        std::string data_checksum() const { return m_data_checksum; }

        /**
         * Compressed integer containing a bitmask of the flags. All unused flags
         * MUST be set to 0. If a decoder sees a flag set that it doesn't
         * recognize, it MUST exit with an error.
         * \sa https://github.com/zchunk/zchunk/blob/99e51afa38c723e7c25834c2c3b305d20ef55d04/zchunk_format.txt#L78-L81 Source
         */
        compressed_integer_t* flags() const { return m_flags; }

        /**
         * Raw integer, don't read this field - access `compression_type`
         * instead.
         */
        compressed_integer_t* compression_type_int() const { return m_compression_type_int; }

        /**
         * If present, it must be at least 1. This is because if there are no
         * optional elements, `has_optional_elements` must be false, and then
         * neither this field nor `optional_elements` is present.
         * \sa https://github.com/zchunk/zchunk/blob/99e51afa38c723e7c25834c2c3b305d20ef55d04/zchunk_format.txt#L99-L102 Source
         */
        compressed_integer_t* num_optional_elements() const { return m_num_optional_elements; }
        std::vector<optional_element_t*>* optional_elements() const { return m_optional_elements; }
        zchunk_t* _root() const { return m__root; }
        zchunk_t::header_without_lead_t* _parent() const { return m__parent; }
    };

private:
    header_lead_t* m_lead;
    header_without_lead_t* m_header_rest;
    std::string m_dict;
    std::vector<std::string>* m_chunks;
    bool n_chunks;

public:
    bool _is_null_chunks() { chunks(); return n_chunks; };

private:
    zchunk_t* m__root;
    kaitai::kstruct* m__parent;
    std::string m__raw_header_rest;
    kaitai::kstream* m__io__raw_header_rest;

public:
    header_lead_t* lead() const { return m_lead; }
    header_without_lead_t* header_rest() const { return m_header_rest; }

    /**
     * Custom dictionary used when compressing each chunk. It's compressed itself
     * without a dictionary.
     * 
     * The official zchunk specification calls this section "Compressed Dict".
     * It's also called a "dictionary chunk". `zck_read_header -c` presents it as
     * "chunk 0" (which is always shown in the chunk table, but can have size 0
     * if the dictionary is not in use).
     */
    std::string dict() const { return m_dict; }

    /**
     * Chunks of data, each compressed with the custom dictionary `dict` (if
     * applicable).
     * 
     * They are not included in a detached header (`.zhr`) file. Detached headers
     * contain the dictionary, but none of the data chunks.
     */
    std::vector<std::string>* chunks() const { return m_chunks; }
    zchunk_t* _root() const { return m__root; }
    kaitai::kstruct* _parent() const { return m__parent; }
    std::string _raw_header_rest() const { return m__raw_header_rest; }
    kaitai::kstream* _io__raw_header_rest() const { return m__io__raw_header_rest; }
};

#endif  // ZCHUNK_H_

zchunk.cpp

// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild

#include "zchunk.h"
#include "kaitai/exceptions.h"
std::set<zchunk_t::checksum_types_t> zchunk_t::_build_values_checksum_types_t() {
    std::set<zchunk_t::checksum_types_t> _t;
    _t.insert(zchunk_t::CHECKSUM_TYPES_SHA1);
    _t.insert(zchunk_t::CHECKSUM_TYPES_SHA256);
    _t.insert(zchunk_t::CHECKSUM_TYPES_SHA512);
    _t.insert(zchunk_t::CHECKSUM_TYPES_SHA512_128);
    return _t;
}
const std::set<zchunk_t::checksum_types_t> zchunk_t::_values_checksum_types_t = zchunk_t::_build_values_checksum_types_t();
bool zchunk_t::_is_defined_checksum_types_t(zchunk_t::checksum_types_t v) {
    return zchunk_t::_values_checksum_types_t.find(v) != zchunk_t::_values_checksum_types_t.end();
}
std::set<zchunk_t::compression_types_t> zchunk_t::_build_values_compression_types_t() {
    std::set<zchunk_t::compression_types_t> _t;
    _t.insert(zchunk_t::COMPRESSION_TYPES_NONE);
    _t.insert(zchunk_t::COMPRESSION_TYPES_ZSTD);
    return _t;
}
const std::set<zchunk_t::compression_types_t> zchunk_t::_values_compression_types_t = zchunk_t::_build_values_compression_types_t();
bool zchunk_t::_is_defined_compression_types_t(zchunk_t::compression_types_t v) {
    return zchunk_t::_values_compression_types_t.find(v) != zchunk_t::_values_compression_types_t.end();
}

zchunk_t::zchunk_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, zchunk_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root ? p__root : this;
    m_lead = 0;
    m_header_rest = 0;
    m__io__raw_header_rest = 0;
    m_chunks = 0;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void zchunk_t::_read() {
    m_lead = new header_lead_t(m__io, this, m__root);
    m__raw_header_rest = m__io->read_bytes(lead()->len_header_rest()->value());
    m__io__raw_header_rest = new kaitai::kstream(m__raw_header_rest);
    m_header_rest = new header_without_lead_t(m__io__raw_header_rest, this, m__root);
    m_dict = m__io->read_bytes(header_rest()->index()->len_dict()->value());
    n_chunks = true;
    if (!(lead()->is_detached_header())) {
        n_chunks = false;
        m_chunks = new std::vector<std::string>();
        const int l_chunks = header_rest()->index()->chunks_metadata()->size();
        for (int i = 0; i < l_chunks; i++) {
            m_chunks->push_back(m__io->read_bytes(header_rest()->index()->chunks_metadata()->at(i)->len_chunk()->value()));
        }
    }
}

zchunk_t::~zchunk_t() {
    _clean_up();
}

void zchunk_t::_clean_up() {
    if (m_lead) {
        delete m_lead; m_lead = 0;
    }
    if (m__io__raw_header_rest) {
        delete m__io__raw_header_rest; m__io__raw_header_rest = 0;
    }
    if (m_header_rest) {
        delete m_header_rest; m_header_rest = 0;
    }
    if (!n_chunks) {
        if (m_chunks) {
            delete m_chunks; m_chunks = 0;
        }
    }
}

zchunk_t::checksum_type_t::checksum_type_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, zchunk_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_raw = 0;
    f_len_checksum = false;
    f_value = false;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void zchunk_t::checksum_type_t::_read() {
    m_raw = new compressed_integer_t(m__io, this, m__root);
    {
        compressed_integer_t* _ = m_raw;
        if (!(len_checksum() != 0)) {
            throw kaitai::validation_expr_error<zchunk_t::compressed_integer_t*>(m_raw, m__io, std::string("/types/checksum_type/seq/0"));
        }
    }
}

zchunk_t::checksum_type_t::~checksum_type_t() {
    _clean_up();
}

void zchunk_t::checksum_type_t::_clean_up() {
    if (m_raw) {
        delete m_raw; m_raw = 0;
    }
}

int8_t zchunk_t::checksum_type_t::len_checksum() {
    if (f_len_checksum)
        return m_len_checksum;
    f_len_checksum = true;
    m_len_checksum = ((value() == zchunk_t::CHECKSUM_TYPES_SHA1) ? (20) : (((value() == zchunk_t::CHECKSUM_TYPES_SHA256) ? (32) : (((value() == zchunk_t::CHECKSUM_TYPES_SHA512) ? (64) : (((value() == zchunk_t::CHECKSUM_TYPES_SHA512_128) ? (16) : (0))))))));
    return m_len_checksum;
}

zchunk_t::checksum_types_t zchunk_t::checksum_type_t::value() {
    if (f_value)
        return m_value;
    f_value = true;
    m_value = static_cast<zchunk_t::checksum_types_t>(raw()->value());
    return m_value;
}

zchunk_t::chunk_t::chunk_t(uint32_t p_len_checksum, bool p_has_data_streams, bool p_has_uncompressed_source, kaitai::kstream* p__io, zchunk_t::index_t* p__parent, zchunk_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_len_checksum = p_len_checksum;
    m_has_data_streams = p_has_data_streams;
    m_has_uncompressed_source = p_has_uncompressed_source;
    m_chunk_stream = 0;
    m_len_chunk = 0;
    m_len_uncompressed_chunk = 0;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void zchunk_t::chunk_t::_read() {
    n_chunk_stream = true;
    if (has_data_streams()) {
        n_chunk_stream = false;
        m_chunk_stream = new compressed_integer_t(m__io, this, m__root);
    }
    m_chunk_checksum = m__io->read_bytes(len_checksum());
    n_uncompressed_chunk_checksum = true;
    if (has_uncompressed_source()) {
        n_uncompressed_chunk_checksum = false;
        m_uncompressed_chunk_checksum = m__io->read_bytes(len_checksum());
    }
    m_len_chunk = new compressed_integer_t(m__io, this, m__root);
    m_len_uncompressed_chunk = new compressed_integer_t(m__io, this, m__root);
}

zchunk_t::chunk_t::~chunk_t() {
    _clean_up();
}

void zchunk_t::chunk_t::_clean_up() {
    if (!n_chunk_stream) {
        if (m_chunk_stream) {
            delete m_chunk_stream; m_chunk_stream = 0;
        }
    }
    if (!n_uncompressed_chunk_checksum) {
    }
    if (m_len_chunk) {
        delete m_len_chunk; m_len_chunk = 0;
    }
    if (m_len_uncompressed_chunk) {
        delete m_len_uncompressed_chunk; m_len_uncompressed_chunk = 0;
    }
}

zchunk_t::compressed_integer_t::compressed_integer_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, zchunk_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_groups = 0;
    f_len = false;
    f_value = false;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void zchunk_t::compressed_integer_t::_read() {
    m_groups = new std::vector<group_t*>();
    {
        int i = 0;
        group_t* _;
        do {
            _ = new group_t(i, m__io, this, m__root);
            m_groups->push_back(_);
            i++;
        } while (!(_->is_last()));
    }
}

zchunk_t::compressed_integer_t::~compressed_integer_t() {
    _clean_up();
}

void zchunk_t::compressed_integer_t::_clean_up() {
    if (m_groups) {
        for (std::vector<group_t*>::iterator it = m_groups->begin(); it != m_groups->end(); ++it) {
            delete *it;
        }
        delete m_groups; m_groups = 0;
    }
}

zchunk_t::compressed_integer_t::group_t::group_t(int32_t p_idx, kaitai::kstream* p__io, zchunk_t::compressed_integer_t* p__parent, zchunk_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_idx = p_idx;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void zchunk_t::compressed_integer_t::group_t::_read() {
    m_is_last = m__io->read_bits_int_be(1);
    if (!(m_is_last == ((idx() == 9) ? (true) : (is_last())))) {
        throw kaitai::validation_not_equal_error<bool>(((idx() == 9) ? (true) : (is_last())), m_is_last, m__io, std::string("/types/compressed_integer/types/group/seq/0"));
    }
    m_value = m__io->read_bits_int_be(7);
    if (!(m_value <= static_cast<uint64_t>(((idx() == 9) ? (1) : (127))))) {
        throw kaitai::validation_greater_than_error<uint64_t>(static_cast<uint64_t>(((idx() == 9) ? (1) : (127))), m_value, m__io, std::string("/types/compressed_integer/types/group/seq/1"));
    }
}

zchunk_t::compressed_integer_t::group_t::~group_t() {
    _clean_up();
}

void zchunk_t::compressed_integer_t::group_t::_clean_up() {
}

int32_t zchunk_t::compressed_integer_t::len() {
    if (f_len)
        return m_len;
    f_len = true;
    m_len = groups()->size();
    return m_len;
}

uint64_t zchunk_t::compressed_integer_t::value() {
    if (f_value)
        return m_value;
    f_value = true;
    m_value = static_cast<uint64_t>(((((((((groups()->at(0)->value() | ((len() >= 2) ? (groups()->at(1)->value() << 7) : (0))) | ((len() >= 3) ? (groups()->at(2)->value() << 14) : (0))) | ((len() >= 4) ? (groups()->at(3)->value() << 21) : (0))) | ((len() >= 5) ? (groups()->at(4)->value() << 28) : (0))) | ((len() >= 6) ? (groups()->at(5)->value() << 35) : (0))) | ((len() >= 7) ? (groups()->at(6)->value() << 42) : (0))) | ((len() >= 8) ? (groups()->at(7)->value() << 49) : (0))) | ((len() >= 9) ? (groups()->at(8)->value() << 56) : (0))) | ((len() >= 10) ? (groups()->at(9)->value() << 63) : (0)));
    return m_value;
}

zchunk_t::header_lead_t::header_lead_t(kaitai::kstream* p__io, zchunk_t* p__parent, zchunk_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_overall_checksum_type = 0;
    m_len_header_rest = 0;
    f_is_detached_header = false;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void zchunk_t::header_lead_t::_read() {
    m_magic = m__io->read_bytes(5);
    if (!( ((m_magic == std::string("\x00\x5A\x43\x4B\x31", 5)) || (m_magic == std::string("\x00\x5A\x48\x52\x31", 5))) )) {
        throw kaitai::validation_not_any_of_error<std::string>(m_magic, m__io, std::string("/types/header_lead/seq/0"));
    }
    m_overall_checksum_type = new checksum_type_t(m__io, this, m__root);
    m_len_header_rest = new compressed_integer_t(m__io, this, m__root);
    m_header_checksum = m__io->read_bytes(overall_checksum_type()->len_checksum());
}

zchunk_t::header_lead_t::~header_lead_t() {
    _clean_up();
}

void zchunk_t::header_lead_t::_clean_up() {
    if (m_overall_checksum_type) {
        delete m_overall_checksum_type; m_overall_checksum_type = 0;
    }
    if (m_len_header_rest) {
        delete m_len_header_rest; m_len_header_rest = 0;
    }
}

bool zchunk_t::header_lead_t::is_detached_header() {
    if (f_is_detached_header)
        return m_is_detached_header;
    f_is_detached_header = true;
    m_is_detached_header = magic().at(2) == 72;
    return m_is_detached_header;
}

zchunk_t::header_without_lead_t::header_without_lead_t(kaitai::kstream* p__io, zchunk_t* p__parent, zchunk_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_preface = 0;
    m_len_index = 0;
    m_index = 0;
    m__io__raw_index = 0;
    m_num_signatures = 0;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void zchunk_t::header_without_lead_t::_read() {
    m_preface = new preface_t(m__io, this, m__root);
    m_len_index = new compressed_integer_t(m__io, this, m__root);
    m__raw_index = m__io->read_bytes(len_index()->value());
    m__io__raw_index = new kaitai::kstream(m__raw_index);
    m_index = new index_t(m__io__raw_index, this, m__root);
    m_num_signatures = new compressed_integer_t(m__io, this, m__root);
    {
        compressed_integer_t* _ = m_num_signatures;
        if (!(_->value() == 0)) {
            throw kaitai::validation_expr_error<zchunk_t::compressed_integer_t*>(m_num_signatures, m__io, std::string("/types/header_without_lead/seq/3"));
        }
    }
}

zchunk_t::header_without_lead_t::~header_without_lead_t() {
    _clean_up();
}

void zchunk_t::header_without_lead_t::_clean_up() {
    if (m_preface) {
        delete m_preface; m_preface = 0;
    }
    if (m_len_index) {
        delete m_len_index; m_len_index = 0;
    }
    if (m__io__raw_index) {
        delete m__io__raw_index; m__io__raw_index = 0;
    }
    if (m_index) {
        delete m_index; m_index = 0;
    }
    if (m_num_signatures) {
        delete m_num_signatures; m_num_signatures = 0;
    }
}

zchunk_t::index_t::index_t(kaitai::kstream* p__io, zchunk_t::header_without_lead_t* p__parent, zchunk_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_chunk_checksum_type = 0;
    m_num_chunks = 0;
    m_dict_stream = 0;
    m_len_dict = 0;
    m_len_uncompressed_dict = 0;
    m_chunks_metadata = 0;
    f_num_data_chunks = false;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void zchunk_t::index_t::_read() {
    m_chunk_checksum_type = new checksum_type_t(m__io, this, m__root);
    m_num_chunks = new compressed_integer_t(m__io, this, m__root);
    {
        compressed_integer_t* _ = m_num_chunks;
        if (!(_->value() >= 1)) {
            throw kaitai::validation_expr_error<zchunk_t::compressed_integer_t*>(m_num_chunks, m__io, std::string("/types/index/seq/1"));
        }
    }
    n_dict_stream = true;
    if (_parent()->preface()->has_data_streams()) {
        n_dict_stream = false;
        m_dict_stream = new compressed_integer_t(m__io, this, m__root);
        {
            compressed_integer_t* _ = m_dict_stream;
            if (!(_->value() == 0)) {
                throw kaitai::validation_expr_error<zchunk_t::compressed_integer_t*>(m_dict_stream, m__io, std::string("/types/index/seq/2"));
            }
        }
    }
    m_dict_checksum = m__io->read_bytes(chunk_checksum_type()->len_checksum());
    n_uncompressed_dict_checksum = true;
    if (_parent()->preface()->has_uncompressed_source()) {
        n_uncompressed_dict_checksum = false;
        m_uncompressed_dict_checksum = m__io->read_bytes(chunk_checksum_type()->len_checksum());
    }
    m_len_dict = new compressed_integer_t(m__io, this, m__root);
    m_len_uncompressed_dict = new compressed_integer_t(m__io, this, m__root);
    m_chunks_metadata = new std::vector<chunk_t*>();
    const int l_chunks_metadata = num_data_chunks();
    for (int i = 0; i < l_chunks_metadata; i++) {
        m_chunks_metadata->push_back(new chunk_t(chunk_checksum_type()->len_checksum(), _parent()->preface()->has_data_streams(), _parent()->preface()->has_uncompressed_source(), m__io, this, m__root));
    }
}

zchunk_t::index_t::~index_t() {
    _clean_up();
}

void zchunk_t::index_t::_clean_up() {
    if (m_chunk_checksum_type) {
        delete m_chunk_checksum_type; m_chunk_checksum_type = 0;
    }
    if (m_num_chunks) {
        delete m_num_chunks; m_num_chunks = 0;
    }
    if (!n_dict_stream) {
        if (m_dict_stream) {
            delete m_dict_stream; m_dict_stream = 0;
        }
    }
    if (!n_uncompressed_dict_checksum) {
    }
    if (m_len_dict) {
        delete m_len_dict; m_len_dict = 0;
    }
    if (m_len_uncompressed_dict) {
        delete m_len_uncompressed_dict; m_len_uncompressed_dict = 0;
    }
    if (m_chunks_metadata) {
        for (std::vector<chunk_t*>::iterator it = m_chunks_metadata->begin(); it != m_chunks_metadata->end(); ++it) {
            delete *it;
        }
        delete m_chunks_metadata; m_chunks_metadata = 0;
    }
}

int32_t zchunk_t::index_t::num_data_chunks() {
    if (f_num_data_chunks)
        return m_num_data_chunks;
    f_num_data_chunks = true;
    m_num_data_chunks = num_chunks()->value() - 1;
    return m_num_data_chunks;
}

zchunk_t::optional_element_t::optional_element_t(kaitai::kstream* p__io, zchunk_t::preface_t* p__parent, zchunk_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_element_id = 0;
    m_len_data = 0;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void zchunk_t::optional_element_t::_read() {
    m_element_id = new compressed_integer_t(m__io, this, m__root);
    m_len_data = new compressed_integer_t(m__io, this, m__root);
    m_data = m__io->read_bytes(len_data()->value());
}

zchunk_t::optional_element_t::~optional_element_t() {
    _clean_up();
}

void zchunk_t::optional_element_t::_clean_up() {
    if (m_element_id) {
        delete m_element_id; m_element_id = 0;
    }
    if (m_len_data) {
        delete m_len_data; m_len_data = 0;
    }
}

zchunk_t::preface_t::preface_t(kaitai::kstream* p__io, zchunk_t::header_without_lead_t* p__parent, zchunk_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_flags = 0;
    m_compression_type_int = 0;
    m_num_optional_elements = 0;
    m_optional_elements = 0;
    f_compression_type = false;
    f_has_data_streams = false;
    f_has_optional_elements = false;
    f_has_uncompressed_source = false;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void zchunk_t::preface_t::_read() {
    m_data_checksum = m__io->read_bytes(_root()->lead()->overall_checksum_type()->len_checksum());
    m_flags = new compressed_integer_t(m__io, this, m__root);
    {
        compressed_integer_t* _ = m_flags;
        if (!(_->value() <= 7)) {
            throw kaitai::validation_expr_error<zchunk_t::compressed_integer_t*>(m_flags, m__io, std::string("/types/preface/seq/1"));
        }
    }
    m_compression_type_int = new compressed_integer_t(m__io, this, m__root);
    {
        compressed_integer_t* _ = m_compression_type_int;
        if (!( ((_->value() == zchunk_t::COMPRESSION_TYPES_NONE) || (_->value() == zchunk_t::COMPRESSION_TYPES_ZSTD)) )) {
            throw kaitai::validation_expr_error<zchunk_t::compressed_integer_t*>(m_compression_type_int, m__io, std::string("/types/preface/seq/2"));
        }
    }
    n_num_optional_elements = true;
    if (has_optional_elements()) {
        n_num_optional_elements = false;
        m_num_optional_elements = new compressed_integer_t(m__io, this, m__root);
        {
            compressed_integer_t* _ = m_num_optional_elements;
            if (!(_->value() >= 1)) {
                throw kaitai::validation_expr_error<zchunk_t::compressed_integer_t*>(m_num_optional_elements, m__io, std::string("/types/preface/seq/3"));
            }
        }
    }
    n_optional_elements = true;
    if (has_optional_elements()) {
        n_optional_elements = false;
        m_optional_elements = new std::vector<optional_element_t*>();
        const int l_optional_elements = num_optional_elements()->value();
        for (int i = 0; i < l_optional_elements; i++) {
            m_optional_elements->push_back(new optional_element_t(m__io, this, m__root));
        }
    }
}

zchunk_t::preface_t::~preface_t() {
    _clean_up();
}

void zchunk_t::preface_t::_clean_up() {
    if (m_flags) {
        delete m_flags; m_flags = 0;
    }
    if (m_compression_type_int) {
        delete m_compression_type_int; m_compression_type_int = 0;
    }
    if (!n_num_optional_elements) {
        if (m_num_optional_elements) {
            delete m_num_optional_elements; m_num_optional_elements = 0;
        }
    }
    if (!n_optional_elements) {
        if (m_optional_elements) {
            for (std::vector<optional_element_t*>::iterator it = m_optional_elements->begin(); it != m_optional_elements->end(); ++it) {
                delete *it;
            }
            delete m_optional_elements; m_optional_elements = 0;
        }
    }
}

zchunk_t::compression_types_t zchunk_t::preface_t::compression_type() {
    if (f_compression_type)
        return m_compression_type;
    f_compression_type = true;
    m_compression_type = static_cast<zchunk_t::compression_types_t>(compression_type_int()->value());
    return m_compression_type;
}

bool zchunk_t::preface_t::has_data_streams() {
    if (f_has_data_streams)
        return m_has_data_streams;
    f_has_data_streams = true;
    m_has_data_streams = (flags()->value() & 1) != 0;
    return m_has_data_streams;
}

bool zchunk_t::preface_t::has_optional_elements() {
    if (f_has_optional_elements)
        return m_has_optional_elements;
    f_has_optional_elements = true;
    m_has_optional_elements = (flags()->value() & 2) != 0;
    return m_has_optional_elements;
}

bool zchunk_t::preface_t::has_uncompressed_source() {
    if (f_has_uncompressed_source)
        return m_has_uncompressed_source;
    f_has_uncompressed_source = true;
    m_has_uncompressed_source = (flags()->value() & 4) != 0;
    return m_has_uncompressed_source;
}