Representing BER TLV data structure in ANSI C?
asn.1, c, data-structures
Solution
From the wiki article:
The type and length are fixed in size (typically 1-4 bytes)
So, I'd change the `nTag` and `nLength` to some fixed-length type. `int`'s size is platform specific and this could cause you some troubles. Fix their sizes for your protocol and use `int8_t`, `int16_t` or `int32_t`, etc. For `nLength`, you may even use unsigned.
As the value could be any type, I'd use `void*` for `pValue`, instead of `unsigned char*`.
How you will use this data structure? How you want to have access to the different TLVs? My point is - do you need linked list? Or, will linked list be the best option for your case/application/purposes/etc?
What I'm trying to say is, that you may remove the `pNext` element and just treat the TLVs as elements of a (dynamically growing) array. This one really depends on your needs.
Most probably, as you're implementing TLVs, you'll need to send them through some connection, right? If so, you need to think about some protocol. I'd do something like this - send the total numbers of TLVs at the very beginning and I would NOT use linked-list, but a dynamic array. You should be careful sending such data structure through the network - the `pNext` pointers will not be valid, they must be reset on the other side of the connection. You also need to carefully send the data, but I guess you know these things. I just wanted to mention them.
EDIT I see you have some troubles understanding what does nested TLV mean.
A nested TLV is just a TLV element, which has value of a type TLV. And this has nothing to do with the "container" of TLVs - dynamic array or linked list.
Here's an untested example, just to get the idea. I'd do this like this:
struct TLV
{
uint32_t nTag;
uint32_t nLength;
void* pValue;
};
// created dynamic array with 3 TLVs:
TLV* pMyTLVs = malloc( 3 * sizeof( struct TLV ) );
// set the first 2 TLVs, some primitives, for example
// ..
// now, set the third TLV to be nested:
pMyTLVs[ 2 ].nTag = ...; // set some tag, that will indicate nested TLV
pMyTLVs[ 2 ].nLength = ...; // set length of the TLV element
pMyTLVs[ 2 ].pValue = malloc( sizeof( struct TLV ) );
// get pointer to the new, _nested_ TLV:
TLV* pNested = (TLV*)pMyTLVs[ 2 ].pValue;
// now use the pNested TLV as an usual TLV:
pNested->nTag = ...;
pNested->nLength = ...;
pNested->pValue = ...;
// of course, pNested is not absolutely necessary, you may use directly
// pMyTLVs[ 2 ].pValue->...;
// but using pNested, makes the code more clear
NOTE: once again, this is not tested code, but I guess you get the idea. Hope that helps.
Problem
Yesterday I came to know about representing information using TLV format. If you were to write a portable BER TLV encoder/decoder in ANSI C, what data structure would you use (*)? Would something like the follwoing do? ``` struct TlvElement { int nTag; int nLength; unsigned char *pValue; // Byte array TlvElement *pNext; }; ``` (*) Unfortunately I can't use C++ and STL for this.