BCD (Binary Coded Decimals) is a common way to encode integer numbers in a way that makes human-readable output somewhat simpler. In this encoding scheme, every decimal digit is encoded as either a single byte (8 bits), or a nibble (half of a byte, 4 bits). This obviously wastes a lot of bits, but it makes translation into human-readable string much easier than traditional binary-to-decimal conversion process, which includes lots of divisions by 10.
For example, encoding integer 31337 in 8-digit, 8 bits per digit, big endian order of digits BCD format yields
00 00 00 03 01 03 03 07
Encoding the same integer as 8-digit, 4 bits per digit, little endian order BCD format would yield:
73 31 30 00
Using this type of encoding in Kaitai Struct is pretty
straightforward: one calls for this type, specifying desired
encoding parameters, and gets result using either as_int
or
as_str
attributes.
This page hosts a formal specification of BCD (Binary Coded Decimals) using Kaitai Struct. This specification can be automatically translated into a variety of programming languages to get a parsing 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.
Using Kaitai Struct in C++/STL usually consists of 3 steps.
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);
#include "kaitai/kaitaistream.h"
kaitai::kstream ks(&is);
bcd_t data(&ks);
After that, one can get various attributes from the structure by invoking getter methods like:
data.as_int() // => Value of this BCD number as integer. Endianness would be selected based on `is_le` parameter given.
#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
/**
* BCD (Binary Coded Decimals) is a common way to encode integer
* numbers in a way that makes human-readable output somewhat
* simpler. In this encoding scheme, every decimal digit is encoded as
* either a single byte (8 bits), or a nibble (half of a byte, 4
* bits). This obviously wastes a lot of bits, but it makes translation
* into human-readable string much easier than traditional
* binary-to-decimal conversion process, which includes lots of
* divisions by 10.
*
* For example, encoding integer 31337 in 8-digit, 8 bits per digit,
* big endian order of digits BCD format yields
*
* ```
* 00 00 00 03 01 03 03 07
* ```
*
* Encoding the same integer as 8-digit, 4 bits per digit, little
* endian order BCD format would yield:
*
* ```
* 73 31 30 00
* ```
*
* Using this type of encoding in Kaitai Struct is pretty
* straightforward: one calls for this type, specifying desired
* encoding parameters, and gets result using either `as_int` or
* `as_str` attributes.
*/
class bcd_t : public kaitai::kstruct {
public:
bcd_t(uint8_t p_num_digits, uint8_t p_bits_per_digit, bool p_is_le, kaitai::kstream* p__io, kaitai::kstruct* p__parent = nullptr, bcd_t* p__root = nullptr);
private:
void _read();
void _clean_up();
public:
~bcd_t();
private:
bool f_as_int;
int32_t m_as_int;
public:
/**
* Value of this BCD number as integer. Endianness would be selected based on `is_le` parameter given.
*/
int32_t as_int();
private:
bool f_as_int_le;
int32_t m_as_int_le;
public:
/**
* Value of this BCD number as integer (treating digit order as little-endian).
*/
int32_t as_int_le();
private:
bool f_last_idx;
int32_t m_last_idx;
public:
/**
* Index of last digit (0-based).
*/
int32_t last_idx();
private:
bool f_as_int_be;
int32_t m_as_int_be;
public:
/**
* Value of this BCD number as integer (treating digit order as big-endian).
*/
int32_t as_int_be();
private:
std::unique_ptr<std::vector<int32_t>> m_digits;
uint8_t m_num_digits;
uint8_t m_bits_per_digit;
bool m_is_le;
bcd_t* m__root;
kaitai::kstruct* m__parent;
public:
std::vector<int32_t>* digits() const { return m_digits.get(); }
/**
* Number of digits in this BCD representation. Only values from 1 to 8 inclusive are supported.
*/
uint8_t num_digits() const { return m_num_digits; }
/**
* Number of bits per digit. Only values of 4 and 8 are supported.
*/
uint8_t bits_per_digit() const { return m_bits_per_digit; }
/**
* Endianness used by this BCD representation. True means little-endian, false is for big-endian.
*/
bool is_le() const { return m_is_le; }
bcd_t* _root() const { return m__root; }
kaitai::kstruct* _parent() const { return m__parent; }
};
// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild
#include "bcd.h"
bcd_t::bcd_t(uint8_t p_num_digits, uint8_t p_bits_per_digit, bool p_is_le, kaitai::kstream* p__io, kaitai::kstruct* p__parent, bcd_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = this;
m_num_digits = p_num_digits;
m_bits_per_digit = p_bits_per_digit;
m_is_le = p_is_le;
m_digits = nullptr;
f_as_int = false;
f_as_int_le = false;
f_last_idx = false;
f_as_int_be = false;
_read();
}
void bcd_t::_read() {
m_digits = std::unique_ptr<std::vector<int32_t>>(new std::vector<int32_t>());
const int l_digits = num_digits();
for (int i = 0; i < l_digits; i++) {
switch (bits_per_digit()) {
case 4: {
m_digits->push_back(std::move(m__io->read_bits_int_be(4)));
break;
}
case 8: {
m_digits->push_back(std::move(m__io->read_u1()));
break;
}
}
}
}
bcd_t::~bcd_t() {
_clean_up();
}
void bcd_t::_clean_up() {
}
int32_t bcd_t::as_int() {
if (f_as_int)
return m_as_int;
m_as_int = ((is_le()) ? (as_int_le()) : (as_int_be()));
f_as_int = true;
return m_as_int;
}
int32_t bcd_t::as_int_le() {
if (f_as_int_le)
return m_as_int_le;
m_as_int_le = (digits()->at(0) + ((num_digits() < 2) ? (0) : (((digits()->at(1) * 10) + ((num_digits() < 3) ? (0) : (((digits()->at(2) * 100) + ((num_digits() < 4) ? (0) : (((digits()->at(3) * 1000) + ((num_digits() < 5) ? (0) : (((digits()->at(4) * 10000) + ((num_digits() < 6) ? (0) : (((digits()->at(5) * 100000) + ((num_digits() < 7) ? (0) : (((digits()->at(6) * 1000000) + ((num_digits() < 8) ? (0) : ((digits()->at(7) * 10000000))))))))))))))))))))));
f_as_int_le = true;
return m_as_int_le;
}
int32_t bcd_t::last_idx() {
if (f_last_idx)
return m_last_idx;
m_last_idx = (num_digits() - 1);
f_last_idx = true;
return m_last_idx;
}
int32_t bcd_t::as_int_be() {
if (f_as_int_be)
return m_as_int_be;
m_as_int_be = (digits()->at(last_idx()) + ((num_digits() < 2) ? (0) : (((digits()->at((last_idx() - 1)) * 10) + ((num_digits() < 3) ? (0) : (((digits()->at((last_idx() - 2)) * 100) + ((num_digits() < 4) ? (0) : (((digits()->at((last_idx() - 3)) * 1000) + ((num_digits() < 5) ? (0) : (((digits()->at((last_idx() - 4)) * 10000) + ((num_digits() < 6) ? (0) : (((digits()->at((last_idx() - 5)) * 100000) + ((num_digits() < 7) ? (0) : (((digits()->at((last_idx() - 6)) * 1000000) + ((num_digits() < 8) ? (0) : ((digits()->at((last_idx() - 7)) * 10000000))))))))))))))))))))));
f_as_int_be = true;
return m_as_int_be;
}