Zchunk: PHP 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.

PHP source code to parse Zchunk

Zchunk.php

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

namespace {
    class Zchunk extends \Kaitai\Struct\Struct {
        public function __construct(\Kaitai\Struct\Stream $_io, ?\Kaitai\Struct\Struct $_parent = null, ?\Zchunk $_root = null) {
            parent::__construct($_io, $_parent, $_root === null ? $this : $_root);
            $this->_read();
        }

        private function _read() {
            $this->_m_lead = new \Zchunk\HeaderLead($this->_io, $this, $this->_root);
            $this->_m__raw_headerRest = $this->_io->readBytes($this->lead()->lenHeaderRest()->value());
            $_io__raw_headerRest = new \Kaitai\Struct\Stream($this->_m__raw_headerRest);
            $this->_m_headerRest = new \Zchunk\HeaderWithoutLead($_io__raw_headerRest, $this, $this->_root);
            $this->_m_dict = $this->_io->readBytes($this->headerRest()->index()->lenDict()->value());
            if (!($this->lead()->isDetachedHeader())) {
                $this->_m_chunks = [];
                $n = count($this->headerRest()->index()->chunksMetadata());
                for ($i = 0; $i < $n; $i++) {
                    $this->_m_chunks[] = $this->_io->readBytes($this->headerRest()->index()->chunksMetadata()[$i]->lenChunk()->value());
                }
            }
        }
        protected $_m_lead;
        protected $_m_headerRest;
        protected $_m_dict;
        protected $_m_chunks;
        protected $_m__raw_headerRest;
        public function lead() { return $this->_m_lead; }
        public function headerRest() { return $this->_m_headerRest; }

        /**
         * 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).
         */
        public function dict() { return $this->_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.
         */
        public function chunks() { return $this->_m_chunks; }
        public function _raw_headerRest() { return $this->_m__raw_headerRest; }
    }
}

namespace Zchunk {
    class ChecksumType extends \Kaitai\Struct\Struct {
        public function __construct(\Kaitai\Struct\Stream $_io, ?\Kaitai\Struct\Struct $_parent = null, ?\Zchunk $_root = null) {
            parent::__construct($_io, $_parent, $_root);
            $this->_read();
        }

        private function _read() {
            $this->_m_raw = new \Zchunk\CompressedInteger($this->_io, $this, $this->_root);
            $_ = $this->_m_raw;
            if (!($this->lenChecksum() != 0)) {
                throw new \Kaitai\Struct\Error\ValidationExprError($this->_m_raw, $this->_io, "/types/checksum_type/seq/0");
            }
        }
        protected $_m_lenChecksum;
        public function lenChecksum() {
            if ($this->_m_lenChecksum !== null)
                return $this->_m_lenChecksum;
            $this->_m_lenChecksum = ($this->value() == \Zchunk\ChecksumTypes::SHA1 ? 20 : ($this->value() == \Zchunk\ChecksumTypes::SHA256 ? 32 : ($this->value() == \Zchunk\ChecksumTypes::SHA512 ? 64 : ($this->value() == \Zchunk\ChecksumTypes::SHA512_128 ? 16 : 0))));
            return $this->_m_lenChecksum;
        }
        protected $_m_value;
        public function value() {
            if ($this->_m_value !== null)
                return $this->_m_value;
            $this->_m_value = $this->raw()->value();
            return $this->_m_value;
        }
        protected $_m_raw;

        /**
         * Raw integer, don't read this field - access `value` instead.
         */
        public function raw() { return $this->_m_raw; }
    }
}

namespace Zchunk {
    class Chunk extends \Kaitai\Struct\Struct {
        public function __construct(int $lenChecksum, bool $hasDataStreams, bool $hasUncompressedSource, \Kaitai\Struct\Stream $_io, ?\Zchunk\Index $_parent = null, ?\Zchunk $_root = null) {
            parent::__construct($_io, $_parent, $_root);
            $this->_m_lenChecksum = $lenChecksum;
            $this->_m_hasDataStreams = $hasDataStreams;
            $this->_m_hasUncompressedSource = $hasUncompressedSource;
            $this->_read();
        }

        private function _read() {
            if ($this->hasDataStreams()) {
                $this->_m_chunkStream = new \Zchunk\CompressedInteger($this->_io, $this, $this->_root);
            }
            $this->_m_chunkChecksum = $this->_io->readBytes($this->lenChecksum());
            if ($this->hasUncompressedSource()) {
                $this->_m_uncompressedChunkChecksum = $this->_io->readBytes($this->lenChecksum());
            }
            $this->_m_lenChunk = new \Zchunk\CompressedInteger($this->_io, $this, $this->_root);
            $this->_m_lenUncompressedChunk = new \Zchunk\CompressedInteger($this->_io, $this, $this->_root);
        }
        protected $_m_chunkStream;
        protected $_m_chunkChecksum;
        protected $_m_uncompressedChunkChecksum;
        protected $_m_lenChunk;
        protected $_m_lenUncompressedChunk;
        protected $_m_lenChecksum;
        protected $_m_hasDataStreams;
        protected $_m_hasUncompressedSource;
        public function chunkStream() { return $this->_m_chunkStream; }
        public function chunkChecksum() { return $this->_m_chunkChecksum; }

        /**
         * Checksum of the uncompressed chunk. Used to detect whether a chunk
         * from an uncompressed source is identical to the compressed chunk.
         */
        public function uncompressedChunkChecksum() { return $this->_m_uncompressedChunkChecksum; }
        public function lenChunk() { return $this->_m_lenChunk; }
        public function lenUncompressedChunk() { return $this->_m_lenUncompressedChunk; }
        public function lenChecksum() { return $this->_m_lenChecksum; }
        public function hasDataStreams() { return $this->_m_hasDataStreams; }
        public function hasUncompressedSource() { return $this->_m_hasUncompressedSource; }
    }
}

/**
 * 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.
 */

namespace Zchunk {
    class CompressedInteger extends \Kaitai\Struct\Struct {
        public function __construct(\Kaitai\Struct\Stream $_io, ?\Kaitai\Struct\Struct $_parent = null, ?\Zchunk $_root = null) {
            parent::__construct($_io, $_parent, $_root);
            $this->_read();
        }

        private function _read() {
            $this->_m_groups = [];
            $i = 0;
            do {
                $_ = new \Zchunk\CompressedInteger\Group($i, $this->_io, $this, $this->_root);
                $this->_m_groups[] = $_;
                $i++;
            } while (!($_->isLast()));
        }
        protected $_m_len;
        public function len() {
            if ($this->_m_len !== null)
                return $this->_m_len;
            $this->_m_len = count($this->groups());
            return $this->_m_len;
        }
        protected $_m_value;

        /**
         * Resulting unsigned value as normal integer
         */
        public function value() {
            if ($this->_m_value !== null)
                return $this->_m_value;
            $this->_m_value = ((((((((($this->groups()[0]->value() | ($this->len() >= 2 ? $this->groups()[1]->value() << 7 : 0)) | ($this->len() >= 3 ? $this->groups()[2]->value() << 14 : 0)) | ($this->len() >= 4 ? $this->groups()[3]->value() << 21 : 0)) | ($this->len() >= 5 ? $this->groups()[4]->value() << 28 : 0)) | ($this->len() >= 6 ? $this->groups()[5]->value() << 35 : 0)) | ($this->len() >= 7 ? $this->groups()[6]->value() << 42 : 0)) | ($this->len() >= 8 ? $this->groups()[7]->value() << 49 : 0)) | ($this->len() >= 9 ? $this->groups()[8]->value() << 56 : 0)) | ($this->len() >= 10 ? $this->groups()[9]->value() << 63 : 0));
            return $this->_m_value;
        }
        protected $_m_groups;
        public function groups() { return $this->_m_groups; }
    }
}

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

namespace Zchunk\CompressedInteger {
    class Group extends \Kaitai\Struct\Struct {
        public function __construct(int $idx, \Kaitai\Struct\Stream $_io, ?\Zchunk\CompressedInteger $_parent = null, ?\Zchunk $_root = null) {
            parent::__construct($_io, $_parent, $_root);
            $this->_m_idx = $idx;
            $this->_read();
        }

        private function _read() {
            $this->_m_isLast = $this->_io->readBitsIntBe(1) != 0;
            if (!($this->_m_isLast == ($this->idx() == 9 ? true : $this->isLast()))) {
                throw new \Kaitai\Struct\Error\ValidationNotEqualError(($this->idx() == 9 ? true : $this->isLast()), $this->_m_isLast, $this->_io, "/types/compressed_integer/types/group/seq/0");
            }
            $this->_m_value = $this->_io->readBitsIntBe(7);
            if (!($this->_m_value <= ($this->idx() == 9 ? 1 : 127))) {
                throw new \Kaitai\Struct\Error\ValidationGreaterThanError(($this->idx() == 9 ? 1 : 127), $this->_m_value, $this->_io, "/types/compressed_integer/types/group/seq/1");
            }
        }
        protected $_m_isLast;
        protected $_m_value;
        protected $_m_idx;

        /**
         * 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]`).
         */
        public function isLast() { return $this->_m_isLast; }

        /**
         * 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).
         */
        public function value() { return $this->_m_value; }
        public function idx() { return $this->_m_idx; }
    }
}

namespace Zchunk {
    class HeaderLead extends \Kaitai\Struct\Struct {
        public function __construct(\Kaitai\Struct\Stream $_io, ?\Zchunk $_parent = null, ?\Zchunk $_root = null) {
            parent::__construct($_io, $_parent, $_root);
            $this->_read();
        }

        private function _read() {
            $this->_m_magic = $this->_io->readBytes(5);
            if (!( (($this->_m_magic == "\x00\x5A\x43\x4B\x31") || ($this->_m_magic == "\x00\x5A\x48\x52\x31")) )) {
                throw new \Kaitai\Struct\Error\ValidationNotAnyOfError($this->_m_magic, $this->_io, "/types/header_lead/seq/0");
            }
            $this->_m_overallChecksumType = new \Zchunk\ChecksumType($this->_io, $this, $this->_root);
            $this->_m_lenHeaderRest = new \Zchunk\CompressedInteger($this->_io, $this, $this->_root);
            $this->_m_headerChecksum = $this->_io->readBytes($this->overallChecksumType()->lenChecksum());
        }
        protected $_m_isDetachedHeader;

        /**
         * Determines whether this file is a zchunk detached header (`.zhr`). If
         * not, it is a complete zchunk file (`.zck`).
         */
        public function isDetachedHeader() {
            if ($this->_m_isDetachedHeader !== null)
                return $this->_m_isDetachedHeader;
            $this->_m_isDetachedHeader = ord($this->magic()[2]) == 72;
            return $this->_m_isDetachedHeader;
        }
        protected $_m_magic;
        protected $_m_overallChecksumType;
        protected $_m_lenHeaderRest;
        protected $_m_headerChecksum;

        /**
         * 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`)
         */
        public function magic() { return $this->_m_magic; }

        /**
         * Type of the checksum used for `header_checksum` and
         * `_root.header_rest.preface.data_checksum`.
         */
        public function overallChecksumType() { return $this->_m_overallChecksumType; }

        /**
         * Size of the header, not including the lead
         */
        public function lenHeaderRest() { return $this->_m_lenHeaderRest; }

        /**
         * 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.
         */
        public function headerChecksum() { return $this->_m_headerChecksum; }
    }
}

namespace Zchunk {
    class HeaderWithoutLead extends \Kaitai\Struct\Struct {
        public function __construct(\Kaitai\Struct\Stream $_io, ?\Zchunk $_parent = null, ?\Zchunk $_root = null) {
            parent::__construct($_io, $_parent, $_root);
            $this->_read();
        }

        private function _read() {
            $this->_m_preface = new \Zchunk\Preface($this->_io, $this, $this->_root);
            $this->_m_lenIndex = new \Zchunk\CompressedInteger($this->_io, $this, $this->_root);
            $this->_m__raw_index = $this->_io->readBytes($this->lenIndex()->value());
            $_io__raw_index = new \Kaitai\Struct\Stream($this->_m__raw_index);
            $this->_m_index = new \Zchunk\Index($_io__raw_index, $this, $this->_root);
            $this->_m_numSignatures = new \Zchunk\CompressedInteger($this->_io, $this, $this->_root);
            $_ = $this->_m_numSignatures;
            if (!($_->value() == 0)) {
                throw new \Kaitai\Struct\Error\ValidationExprError($this->_m_numSignatures, $this->_io, "/types/header_without_lead/seq/3");
            }
        }
        protected $_m_preface;
        protected $_m_lenIndex;
        protected $_m_index;
        protected $_m_numSignatures;
        protected $_m__raw_index;
        public function preface() { return $this->_m_preface; }
        public function lenIndex() { return $this->_m_lenIndex; }
        public function index() { return $this->_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>.
         */
        public function numSignatures() { return $this->_m_numSignatures; }
        public function _raw_index() { return $this->_m__raw_index; }
    }
}

namespace Zchunk {
    class Index extends \Kaitai\Struct\Struct {
        public function __construct(\Kaitai\Struct\Stream $_io, ?\Zchunk\HeaderWithoutLead $_parent = null, ?\Zchunk $_root = null) {
            parent::__construct($_io, $_parent, $_root);
            $this->_read();
        }

        private function _read() {
            $this->_m_chunkChecksumType = new \Zchunk\ChecksumType($this->_io, $this, $this->_root);
            $this->_m_numChunks = new \Zchunk\CompressedInteger($this->_io, $this, $this->_root);
            $_ = $this->_m_numChunks;
            if (!($_->value() >= 1)) {
                throw new \Kaitai\Struct\Error\ValidationExprError($this->_m_numChunks, $this->_io, "/types/index/seq/1");
            }
            if ($this->_parent()->preface()->hasDataStreams()) {
                $this->_m_dictStream = new \Zchunk\CompressedInteger($this->_io, $this, $this->_root);
                $_ = $this->_m_dictStream;
                if (!($_->value() == 0)) {
                    throw new \Kaitai\Struct\Error\ValidationExprError($this->_m_dictStream, $this->_io, "/types/index/seq/2");
                }
            }
            $this->_m_dictChecksum = $this->_io->readBytes($this->chunkChecksumType()->lenChecksum());
            if ($this->_parent()->preface()->hasUncompressedSource()) {
                $this->_m_uncompressedDictChecksum = $this->_io->readBytes($this->chunkChecksumType()->lenChecksum());
            }
            $this->_m_lenDict = new \Zchunk\CompressedInteger($this->_io, $this, $this->_root);
            $this->_m_lenUncompressedDict = new \Zchunk\CompressedInteger($this->_io, $this, $this->_root);
            $this->_m_chunksMetadata = [];
            $n = $this->numDataChunks();
            for ($i = 0; $i < $n; $i++) {
                $this->_m_chunksMetadata[] = new \Zchunk\Chunk($this->chunkChecksumType()->lenChecksum(), $this->_parent()->preface()->hasDataStreams(), $this->_parent()->preface()->hasUncompressedSource(), $this->_io, $this, $this->_root);
            }
        }
        protected $_m_numDataChunks;

        /**
         * Number of data chunks. `num_chunks` counts the dictionary as chunk 0,
         * so it is one greater than this number.
         */
        public function numDataChunks() {
            if ($this->_m_numDataChunks !== null)
                return $this->_m_numDataChunks;
            $this->_m_numDataChunks = $this->numChunks()->value() - 1;
            return $this->_m_numDataChunks;
        }
        protected $_m_chunkChecksumType;
        protected $_m_numChunks;
        protected $_m_dictStream;
        protected $_m_dictChecksum;
        protected $_m_uncompressedDictChecksum;
        protected $_m_lenDict;
        protected $_m_lenUncompressedDict;
        protected $_m_chunksMetadata;

        /**
         * Type of the checksum used for `dict_checksum` and for all
         * `chunks_metadata[...].chunk_checksum` and
         * `chunks_metadata[...].uncompressed_chunk_checksum`.
         */
        public function chunkChecksumType() { return $this->_m_chunkChecksumType; }

        /**
         * 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).
         */
        public function numChunks() { return $this->_m_numChunks; }

        /**
         * If present, it must always be 0.
         */
        public function dictStream() { return $this->_m_dictStream; }
        public function dictChecksum() { return $this->_m_dictChecksum; }

        /**
         * Checksum of the uncompressed dictionary. It has no real use, as the
         * uncompressed source won't have a dictionary.
         */
        public function uncompressedDictChecksum() { return $this->_m_uncompressedDictChecksum; }
        public function lenDict() { return $this->_m_lenDict; }
        public function lenUncompressedDict() { return $this->_m_lenUncompressedDict; }

        /**
         * 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`.
         */
        public function chunksMetadata() { return $this->_m_chunksMetadata; }
    }
}

namespace Zchunk {
    class OptionalElement extends \Kaitai\Struct\Struct {
        public function __construct(\Kaitai\Struct\Stream $_io, ?\Zchunk\Preface $_parent = null, ?\Zchunk $_root = null) {
            parent::__construct($_io, $_parent, $_root);
            $this->_read();
        }

        private function _read() {
            $this->_m_elementId = new \Zchunk\CompressedInteger($this->_io, $this, $this->_root);
            $this->_m_lenData = new \Zchunk\CompressedInteger($this->_io, $this, $this->_root);
            $this->_m_data = $this->_io->readBytes($this->lenData()->value());
        }
        protected $_m_elementId;
        protected $_m_lenData;
        protected $_m_data;
        public function elementId() { return $this->_m_elementId; }
        public function lenData() { return $this->_m_lenData; }
        public function data() { return $this->_m_data; }
    }
}

namespace Zchunk {
    class Preface extends \Kaitai\Struct\Struct {
        public function __construct(\Kaitai\Struct\Stream $_io, ?\Zchunk\HeaderWithoutLead $_parent = null, ?\Zchunk $_root = null) {
            parent::__construct($_io, $_parent, $_root);
            $this->_read();
        }

        private function _read() {
            $this->_m_dataChecksum = $this->_io->readBytes($this->_root()->lead()->overallChecksumType()->lenChecksum());
            $this->_m_flags = new \Zchunk\CompressedInteger($this->_io, $this, $this->_root);
            $_ = $this->_m_flags;
            if (!($_->value() <= 7)) {
                throw new \Kaitai\Struct\Error\ValidationExprError($this->_m_flags, $this->_io, "/types/preface/seq/1");
            }
            $this->_m_compressionTypeInt = new \Zchunk\CompressedInteger($this->_io, $this, $this->_root);
            $_ = $this->_m_compressionTypeInt;
            if (!( (($_->value() == \Zchunk\CompressionTypes::NONE) || ($_->value() == \Zchunk\CompressionTypes::ZSTD)) )) {
                throw new \Kaitai\Struct\Error\ValidationExprError($this->_m_compressionTypeInt, $this->_io, "/types/preface/seq/2");
            }
            if ($this->hasOptionalElements()) {
                $this->_m_numOptionalElements = new \Zchunk\CompressedInteger($this->_io, $this, $this->_root);
                $_ = $this->_m_numOptionalElements;
                if (!($_->value() >= 1)) {
                    throw new \Kaitai\Struct\Error\ValidationExprError($this->_m_numOptionalElements, $this->_io, "/types/preface/seq/3");
                }
            }
            if ($this->hasOptionalElements()) {
                $this->_m_optionalElements = [];
                $n = $this->numOptionalElements()->value();
                for ($i = 0; $i < $n; $i++) {
                    $this->_m_optionalElements[] = new \Zchunk\OptionalElement($this->_io, $this, $this->_root);
                }
            }
        }
        protected $_m_compressionType;
        public function compressionType() {
            if ($this->_m_compressionType !== null)
                return $this->_m_compressionType;
            $this->_m_compressionType = $this->compressionTypeInt()->value();
            return $this->_m_compressionType;
        }
        protected $_m_hasDataStreams;
        public function hasDataStreams() {
            if ($this->_m_hasDataStreams !== null)
                return $this->_m_hasDataStreams;
            $this->_m_hasDataStreams = ($this->flags()->value() & 1) != 0;
            return $this->_m_hasDataStreams;
        }
        protected $_m_hasOptionalElements;
        public function hasOptionalElements() {
            if ($this->_m_hasOptionalElements !== null)
                return $this->_m_hasOptionalElements;
            $this->_m_hasOptionalElements = ($this->flags()->value() & 2) != 0;
            return $this->_m_hasOptionalElements;
        }
        protected $_m_hasUncompressedSource;

        /**
         * The file may be applied against an uncompressed source. This adds an
         * uncompressed checksum to every index entry, including the dictionary.
         */
        public function hasUncompressedSource() {
            if ($this->_m_hasUncompressedSource !== null)
                return $this->_m_hasUncompressedSource;
            $this->_m_hasUncompressedSource = ($this->flags()->value() & 4) != 0;
            return $this->_m_hasUncompressedSource;
        }
        protected $_m_dataChecksum;
        protected $_m_flags;
        protected $_m_compressionTypeInt;
        protected $_m_numOptionalElements;
        protected $_m_optionalElements;

        /**
         * 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).
         */
        public function dataChecksum() { return $this->_m_dataChecksum; }

        /**
         * 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.
         */
        public function flags() { return $this->_m_flags; }

        /**
         * Raw integer, don't read this field - access `compression_type`
         * instead.
         */
        public function compressionTypeInt() { return $this->_m_compressionTypeInt; }

        /**
         * 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.
         */
        public function numOptionalElements() { return $this->_m_numOptionalElements; }
        public function optionalElements() { return $this->_m_optionalElements; }
    }
}

namespace Zchunk {
    class ChecksumTypes {
        const SHA1 = 0;
        const SHA256 = 1;
        const SHA512 = 2;
        const SHA512_128 = 3;

        private const _VALUES = [0 => true, 1 => true, 2 => true, 3 => true];

        public static function isDefined(int $v): bool {
            return isset(self::_VALUES[$v]);
        }
    }
}

namespace Zchunk {
    class CompressionTypes {
        const NONE = 0;
        const ZSTD = 2;

        private const _VALUES = [0 => true, 2 => true];

        public static function isDefined(int $v): bool {
            return isset(self::_VALUES[$v]);
        }
    }
}