0

I am parsing a binary header (std::vector<unsigned char>) and need to extract four unsigned integers.

I will also sometimes need to extract unsigned short as well (for other headers) so generic solutions are preferable.

How can I convert a slice of a std::vector into a integer?

Here is what I've tried:

class PacketHeader {
public:
    static const unsigned short LENGTH = 16;

    PacketHeader(std::vector<unsigned char> &binary_data) {
      this->timestamp_seconds = ntohs(*reinterpret_cast<const unsigned int *>(&binary_data[0]));
      this->timestamp_ms_or_ns = ntohs(*reinterpret_cast<const unsigned int *>(&binary_data[4]));
      this->packet_data_length = ntohs(*reinterpret_cast<const unsigned int *>(&binary_data[8]));
      this->untruncated_packet_data_length = ntohs(*reinterpret_cast<const unsigned int *>(&binary_data[12]));
    }

    unsigned int get_timestamp_seconds();
    unsigned int get_timestamp_ms_or_ns();
    unsigned int get_packet_data_length();
    unsigned int get_untruncated_packet_data_length();
private:
    unsigned int timestamp_seconds;
    unsigned int timestamp_ms_or_ns;
    unsigned int packet_data_length;
    unsigned int untruncated_packet_data_length;
};
collenjones
  • 510
  • 5
  • 19
  • Beware of strict aliasing rule. –  Oct 22 '18 at 05:41
  • I would highly suggest to write a serialiser / deserialiser for packing data into std::vector of bytes, rather than manually reinterpreting each member variable into byte location x. It will be allow you to not only serialise any data, but also serialise classes / structs inside other classes / structs - **automatically** – g-radam Oct 22 '18 at 05:44
  • Another solution: https://stackoverflow.com/questions/52921379/c-populate-a-struct-with-data-from-a-buffer – Galik Oct 22 '18 at 06:12

2 Answers2

0

Of course after I post this question, I find a solution that works.

Here is what I found from here:

template <typename T>
T extract(const vector<unsigned char> &v, int pos)
{
  T value;
  memcpy(&value, &v[pos], sizeof(T));
  return value;
}
collenjones
  • 510
  • 5
  • 19
0

Create a Serialiser / Deserialiser to automatically pack data into byte arrays. Below I've quickly whipped up a serialiser which just serialises integral types. You can extend this to strings, vectors, other objects (use recursion).

using Buffer = std::vector<int>;

/*! 
 * Serialise all primitive data types (Intergrals: 8, 16, 32, 64)
 */
template <typename T>
void serialise(Buffer& buffer, uint32_t& offset, const T& data)
{
    uint32_t size = sizeof(data);           // Get size for memcpy
    buffer.resize(buffer.size()+size);      // Ensure data will fit
    memcpy( &buffer[offset], &data, sizeof(data) ); // Copy data (use ntohs aswell)
    offset += size;                         // Increase offset for next item
}

/*!
 * Primitive Serialiser class which should be applied to elements in a visitor-
 * pattern type construct. Fills buffer with packed binary data of elements.
 */
class Serialiser
{
    public:
        Buffer& buffer;   // resulting byte buffer (just a std::vector<uint8_t>)
        uint32_t offset;  // offset use internally

    public:
        Serialiser(Buffer& _buffer) : buffer(_buffer), offset(0)
        {}

        // Serialise singular data type
        template <typename T>
        operator() (const T& data)
        {
            serialise(buffer, offset, data);
        }

};

Now to use it!

struct PacketHeader
{
    private:
        uint32_t timestamp_seconds;
        uint32_t timestamp_ms_or_ns;

    public:
        PacketHeader()=default;
        // .. Extra constructors if you want?

    private:
        template<typename SER>
        void serialise(SER& ser)
        {
           ser(timestamp_seconds);
           ser(timestamp_ms_or_ns);
        }
};

int main()
{
    // Create Packet Struct with data?
    PacketHeader packet(...);

    Buffer buffer;
    Serialiser serialiser(buffer);

    // Serialise the packet INTO the buffer
    packet.serialise(serialiser);

    // DONE! Read data out
    // .. print bytes of buffer

    return 0;
}

So the Serialiser class applies its ()operator to each member variable in the packet class, via the PacketHeader::serialise method. Every method it applies the operator to, it will copy that variable into the std::vector (the buffer).

To deserialise, You just create a deserialiser class which does the reverse memcpy, the packetHeader class doest need to change.

g-radam
  • 527
  • 1
  • 7
  • 20