ruby_marshal: C++11/STL parsing library

Ruby's Marshal module allows serialization and deserialization of many standard and arbitrary Ruby objects in a compact binary format. It is relatively fast, available in stdlibs standard and allows conservation of language-specific properties (such as symbols or encoding-aware strings).

Feature-wise, it is comparable to other language-specific implementations, such as:

From internal perspective, serialized stream consists of a simple magic header and a record.

KS implementation details

License: CC0-1.0

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

Usage

Runtime library

All parsing code for C++11/STL generated by Kaitai Struct depends on the C++/STL runtime library. You have to install it before you can parse 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.bin", 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:
    ruby_marshal_t data(&ks);
    

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

data.version() // => get version

C++11/STL source code to parse ruby_marshal

ruby_marshal.h

#pragma once

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

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

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

/**
 * Ruby's Marshal module allows serialization and deserialization of
 * many standard and arbitrary Ruby objects in a compact binary
 * format. It is relatively fast, available in stdlibs standard and
 * allows conservation of language-specific properties (such as symbols
 * or encoding-aware strings).
 * 
 * Feature-wise, it is comparable to other language-specific
 * implementations, such as:
 * 
 * * Java's
 *   [Serializable](https://docs.oracle.com/javase/8/docs/api/java/io/Serializable.html)
 * * .NET
 *   [BinaryFormatter](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.serialization.formatters.binary.binaryformatter?view=net-7.0)
 * * Python's
 *   [marshal](https://docs.python.org/3/library/marshal.html),
 *   [pickle](https://docs.python.org/3/library/pickle.html) and
 *   [shelve](https://docs.python.org/3/library/shelve.html)
 * 
 * From internal perspective, serialized stream consists of a simple
 * magic header and a record.
 * \sa https://docs.ruby-lang.org/en/2.4.0/marshal_rdoc.html#label-Stream+Format Source
 */

class ruby_marshal_t : public kaitai::kstruct {

public:
    class ruby_array_t;
    class bignum_t;
    class ruby_struct_t;
    class ruby_symbol_t;
    class packed_int_t;
    class pair_t;
    class instance_var_t;
    class record_t;
    class ruby_hash_t;
    class ruby_string_t;

    enum codes_t {
        CODES_RUBY_STRING = 34,
        CODES_CONST_NIL = 48,
        CODES_RUBY_SYMBOL = 58,
        CODES_RUBY_SYMBOL_LINK = 59,
        CODES_RUBY_OBJECT_LINK = 64,
        CODES_CONST_FALSE = 70,
        CODES_INSTANCE_VAR = 73,
        CODES_RUBY_STRUCT = 83,
        CODES_CONST_TRUE = 84,
        CODES_RUBY_ARRAY = 91,
        CODES_PACKED_INT = 105,
        CODES_BIGNUM = 108,
        CODES_RUBY_HASH = 123
    };

    ruby_marshal_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = nullptr, ruby_marshal_t* p__root = nullptr);

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

public:
    ~ruby_marshal_t();

    class ruby_array_t : public kaitai::kstruct {

    public:

        ruby_array_t(kaitai::kstream* p__io, ruby_marshal_t::record_t* p__parent = nullptr, ruby_marshal_t* p__root = nullptr);

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

    public:
        ~ruby_array_t();

    private:
        std::unique_ptr<packed_int_t> m_num_elements;
        std::unique_ptr<std::vector<std::unique_ptr<record_t>>> m_elements;
        ruby_marshal_t* m__root;
        ruby_marshal_t::record_t* m__parent;

    public:
        packed_int_t* num_elements() const { return m_num_elements.get(); }
        std::vector<std::unique_ptr<record_t>>* elements() const { return m_elements.get(); }
        ruby_marshal_t* _root() const { return m__root; }
        ruby_marshal_t::record_t* _parent() const { return m__parent; }
    };

    /**
     * \sa https://docs.ruby-lang.org/en/2.4.0/marshal_rdoc.html#label-Bignum Source
     */

    class bignum_t : public kaitai::kstruct {

    public:

        bignum_t(kaitai::kstream* p__io, ruby_marshal_t::record_t* p__parent = nullptr, ruby_marshal_t* p__root = nullptr);

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

    public:
        ~bignum_t();

    private:
        uint8_t m_sign;
        std::unique_ptr<packed_int_t> m_len_div_2;
        std::string m_body;
        ruby_marshal_t* m__root;
        ruby_marshal_t::record_t* m__parent;

    public:

        /**
         * A single byte containing `+` for a positive value or `-` for a negative value.
         */
        uint8_t sign() const { return m_sign; }

        /**
         * Length of bignum body, divided by 2.
         */
        packed_int_t* len_div_2() const { return m_len_div_2.get(); }

        /**
         * Bytes that represent the number, see ruby-lang.org docs for reconstruction algorithm.
         */
        std::string body() const { return m_body; }
        ruby_marshal_t* _root() const { return m__root; }
        ruby_marshal_t::record_t* _parent() const { return m__parent; }
    };

    /**
     * \sa https://docs.ruby-lang.org/en/2.4.0/marshal_rdoc.html#label-Struct Source
     */

    class ruby_struct_t : public kaitai::kstruct {

    public:

        ruby_struct_t(kaitai::kstream* p__io, ruby_marshal_t::record_t* p__parent = nullptr, ruby_marshal_t* p__root = nullptr);

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

    public:
        ~ruby_struct_t();

    private:
        std::unique_ptr<record_t> m_name;
        std::unique_ptr<packed_int_t> m_num_members;
        std::unique_ptr<std::vector<std::unique_ptr<pair_t>>> m_members;
        ruby_marshal_t* m__root;
        ruby_marshal_t::record_t* m__parent;

    public:

        /**
         * Symbol containing the name of the struct.
         */
        record_t* name() const { return m_name.get(); }

        /**
         * Number of members in a struct
         */
        packed_int_t* num_members() const { return m_num_members.get(); }
        std::vector<std::unique_ptr<pair_t>>* members() const { return m_members.get(); }
        ruby_marshal_t* _root() const { return m__root; }
        ruby_marshal_t::record_t* _parent() const { return m__parent; }
    };

    /**
     * \sa https://docs.ruby-lang.org/en/2.4.0/marshal_rdoc.html#label-Symbols+and+Byte+Sequence Source
     */

    class ruby_symbol_t : public kaitai::kstruct {

    public:

        ruby_symbol_t(kaitai::kstream* p__io, ruby_marshal_t::record_t* p__parent = nullptr, ruby_marshal_t* p__root = nullptr);

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

    public:
        ~ruby_symbol_t();

    private:
        std::unique_ptr<packed_int_t> m_len;
        std::string m_name;
        ruby_marshal_t* m__root;
        ruby_marshal_t::record_t* m__parent;

    public:
        packed_int_t* len() const { return m_len.get(); }
        std::string name() const { return m_name; }
        ruby_marshal_t* _root() const { return m__root; }
        ruby_marshal_t::record_t* _parent() const { return m__parent; }
    };

    /**
     * Ruby uses sophisticated system to pack integers: first `code`
     * byte either determines packing scheme or carries encoded
     * immediate value (thus allowing smaller values from -123 to 122
     * (inclusive) to take only one byte. There are 11 encoding schemes
     * in total:
     * 
     * * 0 is encoded specially (as 0)
     * * 1..122 are encoded as immediate value with a shift
     * * 123..255 are encoded with code of 0x01 and 1 extra byte
     * * 0x100..0xffff are encoded with code of 0x02 and 2 extra bytes
     * * 0x10000..0xffffff are encoded with code of 0x03 and 3 extra
     *   bytes
     * * 0x1000000..0xffffffff are encoded with code of 0x04 and 4
     *   extra bytes
     * * -123..-1 are encoded as immediate value with another shift
     * * -256..-124 are encoded with code of 0xff and 1 extra byte
     * * -0x10000..-257 are encoded with code of 0xfe and 2 extra bytes
     * * -0x1000000..0x10001 are encoded with code of 0xfd and 3 extra
     *    bytes
     * * -0x40000000..-0x1000001 are encoded with code of 0xfc and 4
     *    extra bytes
     * 
     * Values beyond that are serialized as bignum (even if they
     * technically might be not Bignum class in Ruby implementation,
     * i.e. if they fit into 64 bits on a 64-bit platform).
     * \sa https://docs.ruby-lang.org/en/2.4.0/marshal_rdoc.html#label-Fixnum+and+long Source
     */

    class packed_int_t : public kaitai::kstruct {

    public:

        packed_int_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = nullptr, ruby_marshal_t* p__root = nullptr);

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

    public:
        ~packed_int_t();

    private:
        bool f_is_immediate;
        bool m_is_immediate;

    public:
        bool is_immediate();

    private:
        bool f_value;
        int32_t m_value;

    public:
        int32_t value();

    private:
        uint8_t m_code;
        uint32_t m_encoded;
        bool n_encoded;

    public:
        bool _is_null_encoded() { encoded(); return n_encoded; };

    private:
        uint8_t m_encoded2;
        bool n_encoded2;

    public:
        bool _is_null_encoded2() { encoded2(); return n_encoded2; };

    private:
        ruby_marshal_t* m__root;
        kaitai::kstruct* m__parent;

    public:
        uint8_t code() const { return m_code; }
        uint32_t encoded() const { return m_encoded; }

        /**
         * One extra byte for 3-byte integers (0x03 and 0xfd), as
         * there is no standard `u3` type in KS.
         */
        uint8_t encoded2() const { return m_encoded2; }
        ruby_marshal_t* _root() const { return m__root; }
        kaitai::kstruct* _parent() const { return m__parent; }
    };

    class pair_t : public kaitai::kstruct {

    public:

        pair_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = nullptr, ruby_marshal_t* p__root = nullptr);

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

    public:
        ~pair_t();

    private:
        std::unique_ptr<record_t> m_key;
        std::unique_ptr<record_t> m_value;
        ruby_marshal_t* m__root;
        kaitai::kstruct* m__parent;

    public:
        record_t* key() const { return m_key.get(); }
        record_t* value() const { return m_value.get(); }
        ruby_marshal_t* _root() const { return m__root; }
        kaitai::kstruct* _parent() const { return m__parent; }
    };

    /**
     * \sa https://docs.ruby-lang.org/en/2.4.0/marshal_rdoc.html#label-Instance+Variables Source
     */

    class instance_var_t : public kaitai::kstruct {

    public:

        instance_var_t(kaitai::kstream* p__io, ruby_marshal_t::record_t* p__parent = nullptr, ruby_marshal_t* p__root = nullptr);

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

    public:
        ~instance_var_t();

    private:
        std::unique_ptr<record_t> m_obj;
        std::unique_ptr<packed_int_t> m_num_vars;
        std::unique_ptr<std::vector<std::unique_ptr<pair_t>>> m_vars;
        ruby_marshal_t* m__root;
        ruby_marshal_t::record_t* m__parent;

    public:
        record_t* obj() const { return m_obj.get(); }
        packed_int_t* num_vars() const { return m_num_vars.get(); }
        std::vector<std::unique_ptr<pair_t>>* vars() const { return m_vars.get(); }
        ruby_marshal_t* _root() const { return m__root; }
        ruby_marshal_t::record_t* _parent() const { return m__parent; }
    };

    /**
     * Each record starts with a single byte that determines its type
     * (`code`) and contents. If necessary, additional info as parsed
     * as `body`, to be determined by `code`.
     */

    class record_t : public kaitai::kstruct {

    public:

        record_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = nullptr, ruby_marshal_t* p__root = nullptr);

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

    public:
        ~record_t();

    private:
        codes_t m_code;
        std::unique_ptr<kaitai::kstruct> m_body;
        bool n_body;

    public:
        bool _is_null_body() { body(); return n_body; };

    private:
        ruby_marshal_t* m__root;
        kaitai::kstruct* m__parent;

    public:
        codes_t code() const { return m_code; }
        kaitai::kstruct* body() const { return m_body.get(); }
        ruby_marshal_t* _root() const { return m__root; }
        kaitai::kstruct* _parent() const { return m__parent; }
    };

    /**
     * \sa https://docs.ruby-lang.org/en/2.4.0/marshal_rdoc.html#label-Hash+and+Hash+with+Default+Value Source
     */

    class ruby_hash_t : public kaitai::kstruct {

    public:

        ruby_hash_t(kaitai::kstream* p__io, ruby_marshal_t::record_t* p__parent = nullptr, ruby_marshal_t* p__root = nullptr);

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

    public:
        ~ruby_hash_t();

    private:
        std::unique_ptr<packed_int_t> m_num_pairs;
        std::unique_ptr<std::vector<std::unique_ptr<pair_t>>> m_pairs;
        ruby_marshal_t* m__root;
        ruby_marshal_t::record_t* m__parent;

    public:
        packed_int_t* num_pairs() const { return m_num_pairs.get(); }
        std::vector<std::unique_ptr<pair_t>>* pairs() const { return m_pairs.get(); }
        ruby_marshal_t* _root() const { return m__root; }
        ruby_marshal_t::record_t* _parent() const { return m__parent; }
    };

    /**
     * \sa https://docs.ruby-lang.org/en/2.4.0/marshal_rdoc.html#label-String Source
     */

    class ruby_string_t : public kaitai::kstruct {

    public:

        ruby_string_t(kaitai::kstream* p__io, ruby_marshal_t::record_t* p__parent = nullptr, ruby_marshal_t* p__root = nullptr);

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

    public:
        ~ruby_string_t();

    private:
        std::unique_ptr<packed_int_t> m_len;
        std::string m_body;
        ruby_marshal_t* m__root;
        ruby_marshal_t::record_t* m__parent;

    public:
        packed_int_t* len() const { return m_len.get(); }
        std::string body() const { return m_body; }
        ruby_marshal_t* _root() const { return m__root; }
        ruby_marshal_t::record_t* _parent() const { return m__parent; }
    };

private:
    std::string m_version;
    std::unique_ptr<record_t> m_records;
    ruby_marshal_t* m__root;
    kaitai::kstruct* m__parent;

public:
    std::string version() const { return m_version; }
    record_t* records() const { return m_records.get(); }
    ruby_marshal_t* _root() const { return m__root; }
    kaitai::kstruct* _parent() const { return m__parent; }
};

ruby_marshal.cpp

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

#include "ruby_marshal.h"
#include "kaitai/exceptions.h"

ruby_marshal_t::ruby_marshal_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, ruby_marshal_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = this;
    m_records = nullptr;
    _read();
}

void ruby_marshal_t::_read() {
    m_version = m__io->read_bytes(2);
    if (!(version() == std::string("\x04\x08", 2))) {
        throw kaitai::validation_not_equal_error<std::string>(std::string("\x04\x08", 2), version(), _io(), std::string("/seq/0"));
    }
    m_records = std::unique_ptr<record_t>(new record_t(m__io, this, m__root));
}

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

void ruby_marshal_t::_clean_up() {
}

ruby_marshal_t::ruby_array_t::ruby_array_t(kaitai::kstream* p__io, ruby_marshal_t::record_t* p__parent, ruby_marshal_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_num_elements = nullptr;
    m_elements = nullptr;
    _read();
}

void ruby_marshal_t::ruby_array_t::_read() {
    m_num_elements = std::unique_ptr<packed_int_t>(new packed_int_t(m__io, this, m__root));
    m_elements = std::unique_ptr<std::vector<std::unique_ptr<record_t>>>(new std::vector<std::unique_ptr<record_t>>());
    const int l_elements = num_elements()->value();
    for (int i = 0; i < l_elements; i++) {
        m_elements->push_back(std::move(std::unique_ptr<record_t>(new record_t(m__io, this, m__root))));
    }
}

ruby_marshal_t::ruby_array_t::~ruby_array_t() {
    _clean_up();
}

void ruby_marshal_t::ruby_array_t::_clean_up() {
}

ruby_marshal_t::bignum_t::bignum_t(kaitai::kstream* p__io, ruby_marshal_t::record_t* p__parent, ruby_marshal_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_len_div_2 = nullptr;
    _read();
}

void ruby_marshal_t::bignum_t::_read() {
    m_sign = m__io->read_u1();
    m_len_div_2 = std::unique_ptr<packed_int_t>(new packed_int_t(m__io, this, m__root));
    m_body = m__io->read_bytes((len_div_2()->value() * 2));
}

ruby_marshal_t::bignum_t::~bignum_t() {
    _clean_up();
}

void ruby_marshal_t::bignum_t::_clean_up() {
}

ruby_marshal_t::ruby_struct_t::ruby_struct_t(kaitai::kstream* p__io, ruby_marshal_t::record_t* p__parent, ruby_marshal_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_name = nullptr;
    m_num_members = nullptr;
    m_members = nullptr;
    _read();
}

void ruby_marshal_t::ruby_struct_t::_read() {
    m_name = std::unique_ptr<record_t>(new record_t(m__io, this, m__root));
    m_num_members = std::unique_ptr<packed_int_t>(new packed_int_t(m__io, this, m__root));
    m_members = std::unique_ptr<std::vector<std::unique_ptr<pair_t>>>(new std::vector<std::unique_ptr<pair_t>>());
    const int l_members = num_members()->value();
    for (int i = 0; i < l_members; i++) {
        m_members->push_back(std::move(std::unique_ptr<pair_t>(new pair_t(m__io, this, m__root))));
    }
}

ruby_marshal_t::ruby_struct_t::~ruby_struct_t() {
    _clean_up();
}

void ruby_marshal_t::ruby_struct_t::_clean_up() {
}

ruby_marshal_t::ruby_symbol_t::ruby_symbol_t(kaitai::kstream* p__io, ruby_marshal_t::record_t* p__parent, ruby_marshal_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_len = nullptr;
    _read();
}

void ruby_marshal_t::ruby_symbol_t::_read() {
    m_len = std::unique_ptr<packed_int_t>(new packed_int_t(m__io, this, m__root));
    m_name = kaitai::kstream::bytes_to_str(m__io->read_bytes(len()->value()), std::string("UTF-8"));
}

ruby_marshal_t::ruby_symbol_t::~ruby_symbol_t() {
    _clean_up();
}

void ruby_marshal_t::ruby_symbol_t::_clean_up() {
}

ruby_marshal_t::packed_int_t::packed_int_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, ruby_marshal_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    f_is_immediate = false;
    f_value = false;
    _read();
}

void ruby_marshal_t::packed_int_t::_read() {
    m_code = m__io->read_u1();
    n_encoded = true;
    switch (code()) {
    case 4: {
        n_encoded = false;
        m_encoded = m__io->read_u4le();
        break;
    }
    case 1: {
        n_encoded = false;
        m_encoded = m__io->read_u1();
        break;
    }
    case 252: {
        n_encoded = false;
        m_encoded = m__io->read_u4le();
        break;
    }
    case 253: {
        n_encoded = false;
        m_encoded = m__io->read_u2le();
        break;
    }
    case 3: {
        n_encoded = false;
        m_encoded = m__io->read_u2le();
        break;
    }
    case 2: {
        n_encoded = false;
        m_encoded = m__io->read_u2le();
        break;
    }
    case 255: {
        n_encoded = false;
        m_encoded = m__io->read_u1();
        break;
    }
    case 254: {
        n_encoded = false;
        m_encoded = m__io->read_u2le();
        break;
    }
    }
    n_encoded2 = true;
    switch (code()) {
    case 3: {
        n_encoded2 = false;
        m_encoded2 = m__io->read_u1();
        break;
    }
    case 253: {
        n_encoded2 = false;
        m_encoded2 = m__io->read_u1();
        break;
    }
    }
}

ruby_marshal_t::packed_int_t::~packed_int_t() {
    _clean_up();
}

void ruby_marshal_t::packed_int_t::_clean_up() {
    if (!n_encoded) {
    }
    if (!n_encoded2) {
    }
}

bool ruby_marshal_t::packed_int_t::is_immediate() {
    if (f_is_immediate)
        return m_is_immediate;
    m_is_immediate =  ((code() > 4) && (code() < 252)) ;
    f_is_immediate = true;
    return m_is_immediate;
}

int32_t ruby_marshal_t::packed_int_t::value() {
    if (f_value)
        return m_value;
    m_value = ((is_immediate()) ? (((code() < 128) ? ((code() - 5)) : ((4 - (~(code()) & 127))))) : (((code() == 0) ? (0) : (((code() == 255) ? ((encoded() - 256)) : (((code() == 254) ? ((encoded() - 65536)) : (((code() == 253) ? ((((encoded2() << 16) | encoded()) - 16777216)) : (((code() == 3) ? (((encoded2() << 16) | encoded())) : (encoded()))))))))))));
    f_value = true;
    return m_value;
}

ruby_marshal_t::pair_t::pair_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, ruby_marshal_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_key = nullptr;
    m_value = nullptr;
    _read();
}

void ruby_marshal_t::pair_t::_read() {
    m_key = std::unique_ptr<record_t>(new record_t(m__io, this, m__root));
    m_value = std::unique_ptr<record_t>(new record_t(m__io, this, m__root));
}

ruby_marshal_t::pair_t::~pair_t() {
    _clean_up();
}

void ruby_marshal_t::pair_t::_clean_up() {
}

ruby_marshal_t::instance_var_t::instance_var_t(kaitai::kstream* p__io, ruby_marshal_t::record_t* p__parent, ruby_marshal_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_obj = nullptr;
    m_num_vars = nullptr;
    m_vars = nullptr;
    _read();
}

void ruby_marshal_t::instance_var_t::_read() {
    m_obj = std::unique_ptr<record_t>(new record_t(m__io, this, m__root));
    m_num_vars = std::unique_ptr<packed_int_t>(new packed_int_t(m__io, this, m__root));
    m_vars = std::unique_ptr<std::vector<std::unique_ptr<pair_t>>>(new std::vector<std::unique_ptr<pair_t>>());
    const int l_vars = num_vars()->value();
    for (int i = 0; i < l_vars; i++) {
        m_vars->push_back(std::move(std::unique_ptr<pair_t>(new pair_t(m__io, this, m__root))));
    }
}

ruby_marshal_t::instance_var_t::~instance_var_t() {
    _clean_up();
}

void ruby_marshal_t::instance_var_t::_clean_up() {
}

ruby_marshal_t::record_t::record_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, ruby_marshal_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    _read();
}

void ruby_marshal_t::record_t::_read() {
    m_code = static_cast<ruby_marshal_t::codes_t>(m__io->read_u1());
    n_body = true;
    switch (code()) {
    case ruby_marshal_t::CODES_PACKED_INT: {
        n_body = false;
        m_body = std::unique_ptr<packed_int_t>(new packed_int_t(m__io, this, m__root));
        break;
    }
    case ruby_marshal_t::CODES_BIGNUM: {
        n_body = false;
        m_body = std::unique_ptr<bignum_t>(new bignum_t(m__io, this, m__root));
        break;
    }
    case ruby_marshal_t::CODES_RUBY_ARRAY: {
        n_body = false;
        m_body = std::unique_ptr<ruby_array_t>(new ruby_array_t(m__io, this, m__root));
        break;
    }
    case ruby_marshal_t::CODES_RUBY_SYMBOL_LINK: {
        n_body = false;
        m_body = std::unique_ptr<packed_int_t>(new packed_int_t(m__io, this, m__root));
        break;
    }
    case ruby_marshal_t::CODES_RUBY_STRUCT: {
        n_body = false;
        m_body = std::unique_ptr<ruby_struct_t>(new ruby_struct_t(m__io, this, m__root));
        break;
    }
    case ruby_marshal_t::CODES_RUBY_STRING: {
        n_body = false;
        m_body = std::unique_ptr<ruby_string_t>(new ruby_string_t(m__io, this, m__root));
        break;
    }
    case ruby_marshal_t::CODES_INSTANCE_VAR: {
        n_body = false;
        m_body = std::unique_ptr<instance_var_t>(new instance_var_t(m__io, this, m__root));
        break;
    }
    case ruby_marshal_t::CODES_RUBY_HASH: {
        n_body = false;
        m_body = std::unique_ptr<ruby_hash_t>(new ruby_hash_t(m__io, this, m__root));
        break;
    }
    case ruby_marshal_t::CODES_RUBY_SYMBOL: {
        n_body = false;
        m_body = std::unique_ptr<ruby_symbol_t>(new ruby_symbol_t(m__io, this, m__root));
        break;
    }
    case ruby_marshal_t::CODES_RUBY_OBJECT_LINK: {
        n_body = false;
        m_body = std::unique_ptr<packed_int_t>(new packed_int_t(m__io, this, m__root));
        break;
    }
    }
}

ruby_marshal_t::record_t::~record_t() {
    _clean_up();
}

void ruby_marshal_t::record_t::_clean_up() {
    if (!n_body) {
    }
}

ruby_marshal_t::ruby_hash_t::ruby_hash_t(kaitai::kstream* p__io, ruby_marshal_t::record_t* p__parent, ruby_marshal_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_num_pairs = nullptr;
    m_pairs = nullptr;
    _read();
}

void ruby_marshal_t::ruby_hash_t::_read() {
    m_num_pairs = std::unique_ptr<packed_int_t>(new packed_int_t(m__io, this, m__root));
    m_pairs = std::unique_ptr<std::vector<std::unique_ptr<pair_t>>>(new std::vector<std::unique_ptr<pair_t>>());
    const int l_pairs = num_pairs()->value();
    for (int i = 0; i < l_pairs; i++) {
        m_pairs->push_back(std::move(std::unique_ptr<pair_t>(new pair_t(m__io, this, m__root))));
    }
}

ruby_marshal_t::ruby_hash_t::~ruby_hash_t() {
    _clean_up();
}

void ruby_marshal_t::ruby_hash_t::_clean_up() {
}

ruby_marshal_t::ruby_string_t::ruby_string_t(kaitai::kstream* p__io, ruby_marshal_t::record_t* p__parent, ruby_marshal_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_len = nullptr;
    _read();
}

void ruby_marshal_t::ruby_string_t::_read() {
    m_len = std::unique_ptr<packed_int_t>(new packed_int_t(m__io, this, m__root));
    m_body = m__io->read_bytes(len()->value());
}

ruby_marshal_t::ruby_string_t::~ruby_string_t() {
    _clean_up();
}

void ruby_marshal_t::ruby_string_t::_clean_up() {
}