Вот начало:
typedef std::vector<uint8_t> byte_buffer;
template <std::size_t N>
void append_fixed_width(byte_buffer& buf, uintmax_t val) {
int shift = ((N - 1) * 8);
while (shift >= 0) {
uintmax_t mask = (0xff << shift);
buf.push_back(uint8_t((val & mask) >> shift));
shift -= 8;
}
}
template <typename IntType>
void append_bytes(byte_buffer& buf, IntType val) {
append_fixed_width<sizeof(IntType)>(buf, uintmax_t(val));
}
int main() { // usage example
byte_buffer bytes;
append_bytes(bytes, 1); // appends sizeof(int) bytes
append_bytes(bytes, 1ul); // appends sizeof(unsigned long) bytes
append_bytes(bytes, 'a'); // appends sizeof(int) bytes :p
append_bytes(bytes, char('a')); // appends 1 byte
return 0;
}
Append_bytes
добавит любой целочисленный тип в байтовый буфер, представленный с помощью std::vector<uint8_t>
. Значения упакованы в порядке байтов big endian * . Если вам нужно изменить это, то настройте append_fixed_width
, чтобы просмотреть значение в другом порядке.
Эти функции создают необработанный байтовый буфер, поэтому, кто бы ни декодировал его, он должен знать, что там находится. IIRC, это то, что делает struct.pack
; другими словами, вызывающий struct.unpack
должен предоставить ту же строку формата. Вы можете написать вариант append_fixed_width
для упаковки TLV вместо этого:
template <typename TagType, typename ValueType>
void append_tlv(byte_buffer& buf, TagType t, ValueType val) {
append_fixed_width<sizeof(TagType)>(buf, uintmax_t(t));
append_fixed_width<sizeof(std::size_t)>(buf, uintmax_t(sizeof(ValueType)));
append_fixed_width<sizeof(ValueType)>(buf, uintmax_t(val));
}
Я бы серьёзно посмотрел на предложение Джереми . Я хотел бы, чтобы он существовал, когда я написал весь двоичный код упаковки, который у меня есть.