BCD (Binary Coded Decimals): GraphViz block diagram (.dot) source

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.

KS implementation details

License: CC0-1.0
Minimal Kaitai Struct required: 0.8

References

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.

GraphViz block diagram source

bcd.dot

digraph {
	rankdir=LR;
	node [shape=plaintext];
	subgraph cluster__bcd {
		label="Bcd";
		graph[style=dotted];

		bcd__seq [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
			<TR><TD BGCOLOR="#E0FFE0">pos</TD><TD BGCOLOR="#E0FFE0">size</TD><TD BGCOLOR="#E0FFE0">type</TD><TD BGCOLOR="#E0FFE0">id</TD></TR>
			<TR><TD PORT="digits_pos">0</TD><TD PORT="digits_size">...</TD><TD>switch (bits_per_digit)</TD><TD PORT="digits_type">digits</TD></TR>
			<TR><TD COLSPAN="4" PORT="digits__repeat">repeat num_digits times</TD></TR>
		</TABLE>>];
		bcd__inst__as_int [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
			<TR><TD BGCOLOR="#E0FFE0">id</TD><TD BGCOLOR="#E0FFE0">value</TD></TR>
			<TR><TD>as_int</TD><TD>(is_le ? as_int_le : as_int_be)</TD></TR>
		</TABLE>>];
		bcd__inst__as_int_le [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
			<TR><TD BGCOLOR="#E0FFE0">id</TD><TD BGCOLOR="#E0FFE0">value</TD></TR>
			<TR><TD>as_int_le</TD><TD>(digits[0] + (num_digits &lt; 2 ? 0 : ((digits[1] * 10) + (num_digits &lt; 3 ? 0 : ((digits[2] * 100) + (num_digits &lt; 4 ? 0 : ((digits[3] * 1000) + (num_digits &lt; 5 ? 0 : ((digits[4] * 10000) + (num_digits &lt; 6 ? 0 : ((digits[5] * 100000) + (num_digits &lt; 7 ? 0 : ((digits[6] * 1000000) + (num_digits &lt; 8 ? 0 : (digits[7] * 10000000)))))))))))))))</TD></TR>
		</TABLE>>];
		bcd__inst__last_idx [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
			<TR><TD BGCOLOR="#E0FFE0">id</TD><TD BGCOLOR="#E0FFE0">value</TD></TR>
			<TR><TD>last_idx</TD><TD>(num_digits - 1)</TD></TR>
		</TABLE>>];
		bcd__inst__as_int_be [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
			<TR><TD BGCOLOR="#E0FFE0">id</TD><TD BGCOLOR="#E0FFE0">value</TD></TR>
			<TR><TD>as_int_be</TD><TD>(digits[last_idx] + (num_digits &lt; 2 ? 0 : ((digits[(last_idx - 1)] * 10) + (num_digits &lt; 3 ? 0 : ((digits[(last_idx - 2)] * 100) + (num_digits &lt; 4 ? 0 : ((digits[(last_idx - 3)] * 1000) + (num_digits &lt; 5 ? 0 : ((digits[(last_idx - 4)] * 10000) + (num_digits &lt; 6 ? 0 : ((digits[(last_idx - 5)] * 100000) + (num_digits &lt; 7 ? 0 : ((digits[(last_idx - 6)] * 1000000) + (num_digits &lt; 8 ? 0 : (digits[(last_idx - 7)] * 10000000)))))))))))))))</TD></TR>
		</TABLE>>];
bcd__seq_digits_switch [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
	<TR><TD BGCOLOR="#F0F2E4">case</TD><TD BGCOLOR="#F0F2E4">type</TD></TR>
</TABLE>>];
	}
	bcd__seq:digits_type -> bcd__seq_digits_switch [style=bold];
	bcd__params:bits_per_digit_type -> bcd__seq:digits_type [color="#404040"];
	bcd__params:num_digits_type -> bcd__seq:digits__repeat [color="#404040"];
	bcd__params:is_le_type -> bcd__inst__as_int [color="#404040"];
	bcd__inst__as_int_le:as_int_le_type -> bcd__inst__as_int [color="#404040"];
	bcd__inst__as_int_be:as_int_be_type -> bcd__inst__as_int [color="#404040"];
	bcd__seq:digits_type -> bcd__inst__as_int_le [color="#404040"];
	bcd__params:num_digits_type -> bcd__inst__as_int_le [color="#404040"];
	bcd__seq:digits_type -> bcd__inst__as_int_le [color="#404040"];
	bcd__params:num_digits_type -> bcd__inst__as_int_le [color="#404040"];
	bcd__seq:digits_type -> bcd__inst__as_int_le [color="#404040"];
	bcd__params:num_digits_type -> bcd__inst__as_int_le [color="#404040"];
	bcd__seq:digits_type -> bcd__inst__as_int_le [color="#404040"];
	bcd__params:num_digits_type -> bcd__inst__as_int_le [color="#404040"];
	bcd__seq:digits_type -> bcd__inst__as_int_le [color="#404040"];
	bcd__params:num_digits_type -> bcd__inst__as_int_le [color="#404040"];
	bcd__seq:digits_type -> bcd__inst__as_int_le [color="#404040"];
	bcd__params:num_digits_type -> bcd__inst__as_int_le [color="#404040"];
	bcd__seq:digits_type -> bcd__inst__as_int_le [color="#404040"];
	bcd__params:num_digits_type -> bcd__inst__as_int_le [color="#404040"];
	bcd__seq:digits_type -> bcd__inst__as_int_le [color="#404040"];
	bcd__params:num_digits_type -> bcd__inst__last_idx [color="#404040"];
	bcd__seq:digits_type -> bcd__inst__as_int_be [color="#404040"];
	bcd__inst__last_idx:last_idx_type -> bcd__inst__as_int_be [color="#404040"];
	bcd__params:num_digits_type -> bcd__inst__as_int_be [color="#404040"];
	bcd__seq:digits_type -> bcd__inst__as_int_be [color="#404040"];
	bcd__inst__last_idx:last_idx_type -> bcd__inst__as_int_be [color="#404040"];
	bcd__params:num_digits_type -> bcd__inst__as_int_be [color="#404040"];
	bcd__seq:digits_type -> bcd__inst__as_int_be [color="#404040"];
	bcd__inst__last_idx:last_idx_type -> bcd__inst__as_int_be [color="#404040"];
	bcd__params:num_digits_type -> bcd__inst__as_int_be [color="#404040"];
	bcd__seq:digits_type -> bcd__inst__as_int_be [color="#404040"];
	bcd__inst__last_idx:last_idx_type -> bcd__inst__as_int_be [color="#404040"];
	bcd__params:num_digits_type -> bcd__inst__as_int_be [color="#404040"];
	bcd__seq:digits_type -> bcd__inst__as_int_be [color="#404040"];
	bcd__inst__last_idx:last_idx_type -> bcd__inst__as_int_be [color="#404040"];
	bcd__params:num_digits_type -> bcd__inst__as_int_be [color="#404040"];
	bcd__seq:digits_type -> bcd__inst__as_int_be [color="#404040"];
	bcd__inst__last_idx:last_idx_type -> bcd__inst__as_int_be [color="#404040"];
	bcd__params:num_digits_type -> bcd__inst__as_int_be [color="#404040"];
	bcd__seq:digits_type -> bcd__inst__as_int_be [color="#404040"];
	bcd__inst__last_idx:last_idx_type -> bcd__inst__as_int_be [color="#404040"];
	bcd__params:num_digits_type -> bcd__inst__as_int_be [color="#404040"];
	bcd__seq:digits_type -> bcd__inst__as_int_be [color="#404040"];
	bcd__inst__last_idx:last_idx_type -> bcd__inst__as_int_be [color="#404040"];
}