JPEG (Joint Photographic Experts Group) File Interchange Format: C++11/STL parsing library

JPEG File Interchange Format, or JFIF, or, more colloquially known as just "JPEG" or "JPG", is a popular 2D bitmap image file format, offering lossy compression which works reasonably well with photographic images.

Format is organized as a container format, serving multiple "segments", each starting with a magic and a marker. JFIF standard dictates order and mandatory apperance of segments:

  • SOI
  • APP0 (with JFIF magic)
  • APP0 (with JFXX magic, optional)
  • everything else
  • SOS
  • JPEG-compressed stream
  • EOI

File extension

["jpg", "jpeg", "jpe", "jif", "jfif", "jfi"]

KS implementation details

License: CC0-1.0

References

This page hosts a formal specification of JPEG (Joint Photographic Experts Group) File Interchange Format 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.jpg", 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:
    jpeg_t data(&ks);
    

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

data.segments() // => get segments

C++11/STL source code to parse JPEG (Joint Photographic Experts Group) File Interchange Format

jpeg.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 "exif.h"
#include <vector>

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

/**
 * JPEG File Interchange Format, or JFIF, or, more colloquially known
 * as just "JPEG" or "JPG", is a popular 2D bitmap image file format,
 * offering lossy compression which works reasonably well with
 * photographic images.
 * 
 * Format is organized as a container format, serving multiple
 * "segments", each starting with a magic and a marker. JFIF standard
 * dictates order and mandatory apperance of segments:
 * 
 * * SOI
 * * APP0 (with JFIF magic)
 * * APP0 (with JFXX magic, optional)
 * * everything else
 * * SOS
 * * JPEG-compressed stream
 * * EOI
 */

class jpeg_t : public kaitai::kstruct {

public:
    class segment_t;
    class segment_sos_t;
    class segment_app1_t;
    class segment_sof0_t;
    class exif_in_jpeg_t;
    class segment_app0_t;

    enum component_id_t {
        COMPONENT_ID_Y = 1,
        COMPONENT_ID_CB = 2,
        COMPONENT_ID_CR = 3,
        COMPONENT_ID_I = 4,
        COMPONENT_ID_Q = 5
    };

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

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

public:
    ~jpeg_t();

    class segment_t : public kaitai::kstruct {

    public:

        enum marker_enum_t {
            MARKER_ENUM_TEM = 1,
            MARKER_ENUM_SOF0 = 192,
            MARKER_ENUM_SOF1 = 193,
            MARKER_ENUM_SOF2 = 194,
            MARKER_ENUM_SOF3 = 195,
            MARKER_ENUM_DHT = 196,
            MARKER_ENUM_SOF5 = 197,
            MARKER_ENUM_SOF6 = 198,
            MARKER_ENUM_SOF7 = 199,
            MARKER_ENUM_SOI = 216,
            MARKER_ENUM_EOI = 217,
            MARKER_ENUM_SOS = 218,
            MARKER_ENUM_DQT = 219,
            MARKER_ENUM_DNL = 220,
            MARKER_ENUM_DRI = 221,
            MARKER_ENUM_DHP = 222,
            MARKER_ENUM_APP0 = 224,
            MARKER_ENUM_APP1 = 225,
            MARKER_ENUM_APP2 = 226,
            MARKER_ENUM_APP3 = 227,
            MARKER_ENUM_APP4 = 228,
            MARKER_ENUM_APP5 = 229,
            MARKER_ENUM_APP6 = 230,
            MARKER_ENUM_APP7 = 231,
            MARKER_ENUM_APP8 = 232,
            MARKER_ENUM_APP9 = 233,
            MARKER_ENUM_APP10 = 234,
            MARKER_ENUM_APP11 = 235,
            MARKER_ENUM_APP12 = 236,
            MARKER_ENUM_APP13 = 237,
            MARKER_ENUM_APP14 = 238,
            MARKER_ENUM_APP15 = 239,
            MARKER_ENUM_COM = 254
        };

        segment_t(kaitai::kstream* p__io, jpeg_t* p__parent = nullptr, jpeg_t* p__root = nullptr);

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

    public:
        ~segment_t();

    private:
        std::string m_magic;
        marker_enum_t m_marker;
        uint16_t m_length;
        bool n_length;

    public:
        bool _is_null_length() { length(); return n_length; };

    private:
        std::unique_ptr<kaitai::kstruct> m_data;
        bool n_data;

    public:
        bool _is_null_data() { data(); return n_data; };

    private:
        std::string m_image_data;
        bool n_image_data;

    public:
        bool _is_null_image_data() { image_data(); return n_image_data; };

    private:
        jpeg_t* m__root;
        jpeg_t* m__parent;
        std::string m__raw_data;
        bool n__raw_data;

    public:
        bool _is_null__raw_data() { _raw_data(); return n__raw_data; };

    private:
        std::unique_ptr<kaitai::kstream> m__io__raw_data;

    public:
        std::string magic() const { return m_magic; }
        marker_enum_t marker() const { return m_marker; }
        uint16_t length() const { return m_length; }
        kaitai::kstruct* data() const { return m_data.get(); }
        std::string image_data() const { return m_image_data; }
        jpeg_t* _root() const { return m__root; }
        jpeg_t* _parent() const { return m__parent; }
        std::string _raw_data() const { return m__raw_data; }
        kaitai::kstream* _io__raw_data() const { return m__io__raw_data.get(); }
    };

    class segment_sos_t : public kaitai::kstruct {

    public:
        class component_t;

        segment_sos_t(kaitai::kstream* p__io, jpeg_t::segment_t* p__parent = nullptr, jpeg_t* p__root = nullptr);

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

    public:
        ~segment_sos_t();

        class component_t : public kaitai::kstruct {

        public:

            component_t(kaitai::kstream* p__io, jpeg_t::segment_sos_t* p__parent = nullptr, jpeg_t* p__root = nullptr);

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

        public:
            ~component_t();

        private:
            component_id_t m_id;
            uint8_t m_huffman_table;
            jpeg_t* m__root;
            jpeg_t::segment_sos_t* m__parent;

        public:

            /**
             * Scan component selector
             */
            component_id_t id() const { return m_id; }
            uint8_t huffman_table() const { return m_huffman_table; }
            jpeg_t* _root() const { return m__root; }
            jpeg_t::segment_sos_t* _parent() const { return m__parent; }
        };

    private:
        uint8_t m_num_components;
        std::unique_ptr<std::vector<std::unique_ptr<component_t>>> m_components;
        uint8_t m_start_spectral_selection;
        uint8_t m_end_spectral;
        uint8_t m_appr_bit_pos;
        jpeg_t* m__root;
        jpeg_t::segment_t* m__parent;

    public:

        /**
         * Number of components in scan
         */
        uint8_t num_components() const { return m_num_components; }

        /**
         * Scan components specification
         */
        std::vector<std::unique_ptr<component_t>>* components() const { return m_components.get(); }

        /**
         * Start of spectral selection or predictor selection
         */
        uint8_t start_spectral_selection() const { return m_start_spectral_selection; }

        /**
         * End of spectral selection
         */
        uint8_t end_spectral() const { return m_end_spectral; }

        /**
         * Successive approximation bit position high + Successive approximation bit position low or point transform
         */
        uint8_t appr_bit_pos() const { return m_appr_bit_pos; }
        jpeg_t* _root() const { return m__root; }
        jpeg_t::segment_t* _parent() const { return m__parent; }
    };

    class segment_app1_t : public kaitai::kstruct {

    public:

        segment_app1_t(kaitai::kstream* p__io, jpeg_t::segment_t* p__parent = nullptr, jpeg_t* p__root = nullptr);

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

    public:
        ~segment_app1_t();

    private:
        std::string m_magic;
        std::unique_ptr<exif_in_jpeg_t> m_body;
        bool n_body;

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

    private:
        jpeg_t* m__root;
        jpeg_t::segment_t* m__parent;

    public:
        std::string magic() const { return m_magic; }
        exif_in_jpeg_t* body() const { return m_body.get(); }
        jpeg_t* _root() const { return m__root; }
        jpeg_t::segment_t* _parent() const { return m__parent; }
    };

    class segment_sof0_t : public kaitai::kstruct {

    public:
        class component_t;

        segment_sof0_t(kaitai::kstream* p__io, jpeg_t::segment_t* p__parent = nullptr, jpeg_t* p__root = nullptr);

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

    public:
        ~segment_sof0_t();

        class component_t : public kaitai::kstruct {

        public:

            component_t(kaitai::kstream* p__io, jpeg_t::segment_sof0_t* p__parent = nullptr, jpeg_t* p__root = nullptr);

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

        public:
            ~component_t();

        private:
            bool f_sampling_x;
            int32_t m_sampling_x;

        public:
            int32_t sampling_x();

        private:
            bool f_sampling_y;
            int32_t m_sampling_y;

        public:
            int32_t sampling_y();

        private:
            component_id_t m_id;
            uint8_t m_sampling_factors;
            uint8_t m_quantization_table_id;
            jpeg_t* m__root;
            jpeg_t::segment_sof0_t* m__parent;

        public:

            /**
             * Component selector
             */
            component_id_t id() const { return m_id; }
            uint8_t sampling_factors() const { return m_sampling_factors; }
            uint8_t quantization_table_id() const { return m_quantization_table_id; }
            jpeg_t* _root() const { return m__root; }
            jpeg_t::segment_sof0_t* _parent() const { return m__parent; }
        };

    private:
        uint8_t m_bits_per_sample;
        uint16_t m_image_height;
        uint16_t m_image_width;
        uint8_t m_num_components;
        std::unique_ptr<std::vector<std::unique_ptr<component_t>>> m_components;
        jpeg_t* m__root;
        jpeg_t::segment_t* m__parent;

    public:
        uint8_t bits_per_sample() const { return m_bits_per_sample; }
        uint16_t image_height() const { return m_image_height; }
        uint16_t image_width() const { return m_image_width; }
        uint8_t num_components() const { return m_num_components; }
        std::vector<std::unique_ptr<component_t>>* components() const { return m_components.get(); }
        jpeg_t* _root() const { return m__root; }
        jpeg_t::segment_t* _parent() const { return m__parent; }
    };

    class exif_in_jpeg_t : public kaitai::kstruct {

    public:

        exif_in_jpeg_t(kaitai::kstream* p__io, jpeg_t::segment_app1_t* p__parent = nullptr, jpeg_t* p__root = nullptr);

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

    public:
        ~exif_in_jpeg_t();

    private:
        std::string m_extra_zero;
        std::unique_ptr<exif_t> m_data;
        jpeg_t* m__root;
        jpeg_t::segment_app1_t* m__parent;
        std::string m__raw_data;
        std::unique_ptr<kaitai::kstream> m__io__raw_data;

    public:
        std::string extra_zero() const { return m_extra_zero; }
        exif_t* data() const { return m_data.get(); }
        jpeg_t* _root() const { return m__root; }
        jpeg_t::segment_app1_t* _parent() const { return m__parent; }
        std::string _raw_data() const { return m__raw_data; }
        kaitai::kstream* _io__raw_data() const { return m__io__raw_data.get(); }
    };

    class segment_app0_t : public kaitai::kstruct {

    public:

        enum density_unit_t {
            DENSITY_UNIT_NO_UNITS = 0,
            DENSITY_UNIT_PIXELS_PER_INCH = 1,
            DENSITY_UNIT_PIXELS_PER_CM = 2
        };

        segment_app0_t(kaitai::kstream* p__io, jpeg_t::segment_t* p__parent = nullptr, jpeg_t* p__root = nullptr);

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

    public:
        ~segment_app0_t();

    private:
        std::string m_magic;
        uint8_t m_version_major;
        uint8_t m_version_minor;
        density_unit_t m_density_units;
        uint16_t m_density_x;
        uint16_t m_density_y;
        uint8_t m_thumbnail_x;
        uint8_t m_thumbnail_y;
        std::string m_thumbnail;
        jpeg_t* m__root;
        jpeg_t::segment_t* m__parent;

    public:
        std::string magic() const { return m_magic; }
        uint8_t version_major() const { return m_version_major; }
        uint8_t version_minor() const { return m_version_minor; }
        density_unit_t density_units() const { return m_density_units; }

        /**
         * Horizontal pixel density. Must not be zero.
         */
        uint16_t density_x() const { return m_density_x; }

        /**
         * Vertical pixel density. Must not be zero.
         */
        uint16_t density_y() const { return m_density_y; }

        /**
         * Horizontal pixel count of the following embedded RGB thumbnail. May be zero.
         */
        uint8_t thumbnail_x() const { return m_thumbnail_x; }

        /**
         * Vertical pixel count of the following embedded RGB thumbnail. May be zero.
         */
        uint8_t thumbnail_y() const { return m_thumbnail_y; }

        /**
         * Uncompressed 24 bit RGB (8 bits per color channel) raster thumbnail data in the order R0, G0, B0, ... Rn, Gn, Bn
         */
        std::string thumbnail() const { return m_thumbnail; }
        jpeg_t* _root() const { return m__root; }
        jpeg_t::segment_t* _parent() const { return m__parent; }
    };

private:
    std::unique_ptr<std::vector<std::unique_ptr<segment_t>>> m_segments;
    jpeg_t* m__root;
    kaitai::kstruct* m__parent;

public:
    std::vector<std::unique_ptr<segment_t>>* segments() const { return m_segments.get(); }
    jpeg_t* _root() const { return m__root; }
    kaitai::kstruct* _parent() const { return m__parent; }
};

jpeg.cpp

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

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

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

void jpeg_t::_read() {
    m_segments = std::unique_ptr<std::vector<std::unique_ptr<segment_t>>>(new std::vector<std::unique_ptr<segment_t>>());
    {
        int i = 0;
        while (!m__io->is_eof()) {
            m_segments->push_back(std::move(std::unique_ptr<segment_t>(new segment_t(m__io, this, m__root))));
            i++;
        }
    }
}

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

void jpeg_t::_clean_up() {
}

jpeg_t::segment_t::segment_t(kaitai::kstream* p__io, jpeg_t* p__parent, jpeg_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m__io__raw_data = nullptr;
    _read();
}

void jpeg_t::segment_t::_read() {
    m_magic = m__io->read_bytes(1);
    if (!(magic() == std::string("\xFF", 1))) {
        throw kaitai::validation_not_equal_error<std::string>(std::string("\xFF", 1), magic(), _io(), std::string("/types/segment/seq/0"));
    }
    m_marker = static_cast<jpeg_t::segment_t::marker_enum_t>(m__io->read_u1());
    n_length = true;
    if ( ((marker() != jpeg_t::segment_t::MARKER_ENUM_SOI) && (marker() != jpeg_t::segment_t::MARKER_ENUM_EOI)) ) {
        n_length = false;
        m_length = m__io->read_u2be();
    }
    n_data = true;
    if ( ((marker() != jpeg_t::segment_t::MARKER_ENUM_SOI) && (marker() != jpeg_t::segment_t::MARKER_ENUM_EOI)) ) {
        n_data = false;
        n_data = true;
        switch (marker()) {
        case jpeg_t::segment_t::MARKER_ENUM_APP1: {
            n_data = false;
            m__raw_data = m__io->read_bytes((length() - 2));
            m__io__raw_data = std::unique_ptr<kaitai::kstream>(new kaitai::kstream(m__raw_data));
            m_data = std::unique_ptr<segment_app1_t>(new segment_app1_t(m__io__raw_data.get(), this, m__root));
            break;
        }
        case jpeg_t::segment_t::MARKER_ENUM_APP0: {
            n_data = false;
            m__raw_data = m__io->read_bytes((length() - 2));
            m__io__raw_data = std::unique_ptr<kaitai::kstream>(new kaitai::kstream(m__raw_data));
            m_data = std::unique_ptr<segment_app0_t>(new segment_app0_t(m__io__raw_data.get(), this, m__root));
            break;
        }
        case jpeg_t::segment_t::MARKER_ENUM_SOF0: {
            n_data = false;
            m__raw_data = m__io->read_bytes((length() - 2));
            m__io__raw_data = std::unique_ptr<kaitai::kstream>(new kaitai::kstream(m__raw_data));
            m_data = std::unique_ptr<segment_sof0_t>(new segment_sof0_t(m__io__raw_data.get(), this, m__root));
            break;
        }
        case jpeg_t::segment_t::MARKER_ENUM_SOS: {
            n_data = false;
            m__raw_data = m__io->read_bytes((length() - 2));
            m__io__raw_data = std::unique_ptr<kaitai::kstream>(new kaitai::kstream(m__raw_data));
            m_data = std::unique_ptr<segment_sos_t>(new segment_sos_t(m__io__raw_data.get(), this, m__root));
            break;
        }
        default: {
            m__raw_data = m__io->read_bytes((length() - 2));
            break;
        }
        }
    }
    n_image_data = true;
    if (marker() == jpeg_t::segment_t::MARKER_ENUM_SOS) {
        n_image_data = false;
        m_image_data = m__io->read_bytes_full();
    }
}

jpeg_t::segment_t::~segment_t() {
    _clean_up();
}

void jpeg_t::segment_t::_clean_up() {
    if (!n_length) {
    }
    if (!n_data) {
    }
    if (!n_image_data) {
    }
}

jpeg_t::segment_sos_t::segment_sos_t(kaitai::kstream* p__io, jpeg_t::segment_t* p__parent, jpeg_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_components = nullptr;
    _read();
}

void jpeg_t::segment_sos_t::_read() {
    m_num_components = m__io->read_u1();
    m_components = std::unique_ptr<std::vector<std::unique_ptr<component_t>>>(new std::vector<std::unique_ptr<component_t>>());
    const int l_components = num_components();
    for (int i = 0; i < l_components; i++) {
        m_components->push_back(std::move(std::unique_ptr<component_t>(new component_t(m__io, this, m__root))));
    }
    m_start_spectral_selection = m__io->read_u1();
    m_end_spectral = m__io->read_u1();
    m_appr_bit_pos = m__io->read_u1();
}

jpeg_t::segment_sos_t::~segment_sos_t() {
    _clean_up();
}

void jpeg_t::segment_sos_t::_clean_up() {
}

jpeg_t::segment_sos_t::component_t::component_t(kaitai::kstream* p__io, jpeg_t::segment_sos_t* p__parent, jpeg_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    _read();
}

void jpeg_t::segment_sos_t::component_t::_read() {
    m_id = static_cast<jpeg_t::component_id_t>(m__io->read_u1());
    m_huffman_table = m__io->read_u1();
}

jpeg_t::segment_sos_t::component_t::~component_t() {
    _clean_up();
}

void jpeg_t::segment_sos_t::component_t::_clean_up() {
}

jpeg_t::segment_app1_t::segment_app1_t(kaitai::kstream* p__io, jpeg_t::segment_t* p__parent, jpeg_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    _read();
}

void jpeg_t::segment_app1_t::_read() {
    m_magic = kaitai::kstream::bytes_to_str(m__io->read_bytes_term(0, false, true, true), std::string("ASCII"));
    n_body = true;
    {
        std::string on = magic();
        if (on == std::string("Exif")) {
            n_body = false;
            m_body = std::unique_ptr<exif_in_jpeg_t>(new exif_in_jpeg_t(m__io, this, m__root));
        }
    }
}

jpeg_t::segment_app1_t::~segment_app1_t() {
    _clean_up();
}

void jpeg_t::segment_app1_t::_clean_up() {
    if (!n_body) {
    }
}

jpeg_t::segment_sof0_t::segment_sof0_t(kaitai::kstream* p__io, jpeg_t::segment_t* p__parent, jpeg_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_components = nullptr;
    _read();
}

void jpeg_t::segment_sof0_t::_read() {
    m_bits_per_sample = m__io->read_u1();
    m_image_height = m__io->read_u2be();
    m_image_width = m__io->read_u2be();
    m_num_components = m__io->read_u1();
    m_components = std::unique_ptr<std::vector<std::unique_ptr<component_t>>>(new std::vector<std::unique_ptr<component_t>>());
    const int l_components = num_components();
    for (int i = 0; i < l_components; i++) {
        m_components->push_back(std::move(std::unique_ptr<component_t>(new component_t(m__io, this, m__root))));
    }
}

jpeg_t::segment_sof0_t::~segment_sof0_t() {
    _clean_up();
}

void jpeg_t::segment_sof0_t::_clean_up() {
}

jpeg_t::segment_sof0_t::component_t::component_t(kaitai::kstream* p__io, jpeg_t::segment_sof0_t* p__parent, jpeg_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    f_sampling_x = false;
    f_sampling_y = false;
    _read();
}

void jpeg_t::segment_sof0_t::component_t::_read() {
    m_id = static_cast<jpeg_t::component_id_t>(m__io->read_u1());
    m_sampling_factors = m__io->read_u1();
    m_quantization_table_id = m__io->read_u1();
}

jpeg_t::segment_sof0_t::component_t::~component_t() {
    _clean_up();
}

void jpeg_t::segment_sof0_t::component_t::_clean_up() {
}

int32_t jpeg_t::segment_sof0_t::component_t::sampling_x() {
    if (f_sampling_x)
        return m_sampling_x;
    m_sampling_x = ((sampling_factors() & 240) >> 4);
    f_sampling_x = true;
    return m_sampling_x;
}

int32_t jpeg_t::segment_sof0_t::component_t::sampling_y() {
    if (f_sampling_y)
        return m_sampling_y;
    m_sampling_y = (sampling_factors() & 15);
    f_sampling_y = true;
    return m_sampling_y;
}

jpeg_t::exif_in_jpeg_t::exif_in_jpeg_t(kaitai::kstream* p__io, jpeg_t::segment_app1_t* p__parent, jpeg_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_data = nullptr;
    m__io__raw_data = nullptr;
    _read();
}

void jpeg_t::exif_in_jpeg_t::_read() {
    m_extra_zero = m__io->read_bytes(1);
    if (!(extra_zero() == std::string("\x00", 1))) {
        throw kaitai::validation_not_equal_error<std::string>(std::string("\x00", 1), extra_zero(), _io(), std::string("/types/exif_in_jpeg/seq/0"));
    }
    m__raw_data = m__io->read_bytes_full();
    m__io__raw_data = std::unique_ptr<kaitai::kstream>(new kaitai::kstream(m__raw_data));
    m_data = std::unique_ptr<exif_t>(new exif_t(m__io__raw_data.get()));
}

jpeg_t::exif_in_jpeg_t::~exif_in_jpeg_t() {
    _clean_up();
}

void jpeg_t::exif_in_jpeg_t::_clean_up() {
}

jpeg_t::segment_app0_t::segment_app0_t(kaitai::kstream* p__io, jpeg_t::segment_t* p__parent, jpeg_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    _read();
}

void jpeg_t::segment_app0_t::_read() {
    m_magic = kaitai::kstream::bytes_to_str(m__io->read_bytes(5), std::string("ASCII"));
    m_version_major = m__io->read_u1();
    m_version_minor = m__io->read_u1();
    m_density_units = static_cast<jpeg_t::segment_app0_t::density_unit_t>(m__io->read_u1());
    m_density_x = m__io->read_u2be();
    m_density_y = m__io->read_u2be();
    m_thumbnail_x = m__io->read_u1();
    m_thumbnail_y = m__io->read_u1();
    m_thumbnail = m__io->read_bytes(((thumbnail_x() * thumbnail_y()) * 3));
}

jpeg_t::segment_app0_t::~segment_app0_t() {
    _clean_up();
}

void jpeg_t::segment_app0_t::_clean_up() {
}