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);
};
-
\$\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\$Attilio– Attilio2016年01月21日 11:16:23 +00:00Commented Jan 21, 2016 at 11:16
1 Answer 1
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 ausing 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, likeint
,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++.