1
\$\begingroup\$

This class is extension of this. Now I have added support to serialize array of TLV objects to byte array and vice versa. I posted also someone may find it useful in future (together with other TLVObject class). Feel free to provide feedback.

Usage:

// Add various values to TLV parser
TLVParser a;
a.AddByteTlv("Bill",9);
a.AddStringTlv("Name","David");
a.AddUINT16Tlv("Age",49);
a.AddUINT16Tlv("Height",129);
a.AddUINT64Tlv("Time",12900000123);

// Convert TLV array to byte array
vector<uint8_t> serializedArray = a.Serialize();

// Reverse - get TLV objects from the byte array
TLVParser b;
b.Deserialize(serializedArray );

Cpp:

TLVParser::TLVParser(void)
{
}


TLVParser::~TLVParser(void)
{
}

// Serialize array of TLV objects to byte array - and return that byte array
vector<uint8_t> TLVParser::Serialize()
{
    // Result will be stored here
    vector<uint8_t> result;

    // Go through each TLV object
    for(uint32_t i = 0 ; i<m_objects.size(); i++)
    {
        // Serialize each TLV object
        vector<uint8_t> tmp = m_objects.at(i).Serialize();
        for(uint32_t j = 0; j<tmp.size(); j++)
        {
            // Add serialized TLV to our main result.
            result.push_back(tmp.at(j));
        }
    }

    // Return result.
    return result;
}


// Reconstruct TLV objects array from byte array
void TLVParser::Deserialize(vector<uint8_t> byteArray)
{
    vector<uint8_t> result;
    uint32_t tlvOffset = 0;

    for(uint32_t i = 0 ; i < byteArray.size(); i += tlvOffset)
    {
        // Get length value of tag name first.
        // We need it to compute offset to the next TLV object in the array
        vector<uint8_t> tagLenArr;
        tagLenArr.push_back(byteArray[i + 1]);
        tagLenArr.push_back(byteArray[i + 2]);
        uint16_t tagLen = TLVObject::LEToUINT16(tagLenArr);

        // Now, get value of data length for this TLV object.
        // We need it to compute offset to the next TLV object in the array
        vector<uint8_t> dataLenArr;
        dataLenArr.push_back(byteArray[i + 1 /*type offset*/ + 2 /*tag len offset*/ + tagLen /*tag name offset */]);
        dataLenArr.push_back(byteArray[i + 1 /*type offset*/ + 2 /*tag len offset*/ + tagLen /*tag name offset */ + 1]);
        dataLenArr.push_back(byteArray[i + 1 /*type offset*/ + 2 /*tag len offset*/ + tagLen /*tag name offset */ + 2]);
        dataLenArr.push_back(byteArray[i + 1 /*type offset*/ + 2 /*tag len offset*/ + tagLen /*tag name offset */ + 3]);
        uint32_t dataLen = TLVObject::LEToUINT32(dataLenArr);

        // Now, copy byte array represenging current TLV object to a temporary byte array.
        // We will deserialize that.
        vector<uint8_t> tempArray;
        for(uint32_t j = 0; j < 1 + 2 + tagLen + 4 + dataLen; j++)
        {
            tempArray.push_back(byteArray[i + j]);
        }

        // Now, deserialize the byte array that we copied above.
        TLVObject o;
        o.Deserialize(tempArray);
        m_objects.push_back(o);

        // Compute offset to the next TLV object in the array.
        tlvOffset = 1 + 2 + tagLen + 4 + dataLen;
    }


}


// Return a specific TLV object which has a given tag.
// If no TLV object with that tag is found, throw and exception.
TLVObject TLVParser::Find(string tag)
{
    if(HasTLV(tag) == false)
        throw runtime_error("No such TLV object found");

    for(uint32_t i = 0; i<m_objects.size(); i++)
    {
        if(m_objects[i].GetTagName() == tag)
            return m_objects[i];
    }

    throw runtime_error("No such TLV object found");
}


// Looks if there is a TLV object which has a given tag.
bool TLVParser::HasTLV(string tag)
{
    for(uint32_t i = 0; i<m_objects.size(); i++)
    {
        if(m_objects[i].GetTagName() == tag)
            return true;
    }

    return false;
}

header:

class TLVParser
{
    vector<TLVObject> m_objects;

public:

    bool HasTLV(string tag);
    TLVObject Find(string tag);

    vector<uint8_t> Serialize();
    void Deserialize(vector<uint8_t> byteArray);

    void AddStringTlv(string tag, string value)
    {
        TLVObject o(tag, value);
        m_objects.push_back(o);
    }

    void AddBlobTlv(string tag, vector<uint8_t> value)
    {
        TLVObject o(tag, value);
        m_objects.push_back(o);
    }

    void AddByteTlv(string tag, uint8_t value)
    {
        TLVObject o(tag, value);
        m_objects.push_back(o);
    }

    void AddUINT16Tlv(string tag, uint16_t value)
    {
        TLVObject o(tag, value);
        m_objects.push_back(o);
    }

    void AddUINT32Tlv(string tag, uint32_t value)
    {
        TLVObject o(tag, value);
        m_objects.push_back(o);
    }

    void AddUINT64Tlv(string tag, uint64_t value)
    {
        TLVObject o(tag, value);
        m_objects.push_back(o);
    }

    TLVParser(void);
    ~TLVParser(void);
};
\$\endgroup\$
1
1
\$\begingroup\$

A few basic suggestions:

  • Don't write void on functions taking zero arguments. That's a requirement in the C language, but not in C++. It serves no purpose besides adding verbosity to the code.

  • Defining empty constructors/destructor is not necessary. The compiler can do a better job supplying the defaults for you.

  • Use range-based for to iterate arrays and collections from back-to-back. It will make your code more concise and even less error prone. You have 3 or 4 loops there that could be using range-based for.

  • I see that you're using the Standard Library names without the std:: prefix everywhere, so you probably have a using namespace std in a header file somewhere. That's a very bad idea that makes your code fragile and less portable.

  • Use references to avoid unnecessary copies. For example, Deserialize() takes a vector by value, making a local copy of all the data when it only needs to iterate the vector. It should probably take it by const reference instead (const std::vector<uint8_t> &). Same is true for other functions taking strings and large objects. If you don't need the local copy, pass by reference. The exception of course are the built-in types, like int, float, etc. They fit in a machine register, so copy is free. This suggestion applies to user-defined types.

  • Serialize() should be a const method. See also: Const methods in C++.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy