2
\$\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);
};
asked Jan 21, 2016 at 8:55
\$\endgroup\$
1
  • \$\begingroup\$ In the serializer, why not use std::insert to add a vector to the other one, instead of appending elements one-by-one? (See stackoverflow.com/questions/2551775/…) \$\endgroup\$ Commented Jan 21, 2016 at 11:16

1 Answer 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++.

answered Jan 21, 2016 at 18:32
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.