昨天我开始了解使用TLV格式表示信息。
如果您要在ANSI C中编写便携式BER TLV编码器/解码器,您将使用哪种数据结构(*)?
会不会像以后那样呢?
struct TlvElement
{
int nTag;
int nLength;
unsigned char *pValue; // Byte array
TlvElement *pNext;
};
(*)不幸的是我不能使用C ++和STL。
答案 0 :(得分:3)
来自维基文章:
类型和长度的大小是固定的(通常为1-4个字节)
因此,我将nTag
和nLength
更改为某个固定长度类型。 int
的大小是特定于平台的,这可能会给您带来一些麻烦。修复协议的大小并使用int8_t
,int16_t
或int32_t
等。对于nLength
,您甚至可以使用无符号。
由于值可以是任何类型,我会void*
使用pValue
,而不是unsigned char*
。
您将如何使用此数据结构?您希望如何访问不同的TLV? 我的观点是 - 你需要链表吗?或者,链接列表是否为您的案例/应用程序/目的/等的最佳选项?
我想说的是,您可以删除pNext
元素并将TLV视为(动态增长的)数组的元素。这个真的取决于你的需求。
最有可能的是,当你实现TLV时,你需要通过某种连接发送它们,对吧?如果是这样,你需要考虑一些协议。我会做这样的事情 - 在最开始发送TLV的总数,我不会使用链表,而是动态数组。
您应该小心通过网络发送此类数据结构 - pNext
指针无效,必须在连接的另一侧重置。
您还需要仔细发送数据,但我想您知道这些事情。我只是想提一下。
编辑我发现您在理解嵌套 TLV的含义时遇到了一些麻烦。
嵌套TLV只是一个TLV元素,其值为TLV类型。这与TLV的“容器”无关 - 动态数组或链表。
这是一个未经测试的示例,只是为了得到这个想法。我会这样做:
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
注意:再一次,这不是经过测试的代码,但我想你明白了。希望有所帮助。
答案 1 :(得分:1)
如果我要用ANSI C编写TLV编码器/解码器,我会选择经过验证,标准化,灵活的数据序列化(即线上)格式:ASN.1 BER,Thrift,etc。
这是一个经典的区域,车轮每天都会被重新发明。聪明人已经想到了高效,可维护和灵活的解决方案;再次经历同样的过程没有什么意义。
例如,如果您的示例中的结构用于序列化,您仍然需要考虑:
int
的大小取决于编译器平台和操作系统)一些现有格式提供了语义和语法之间的分离;其他允许您自动为数据方案生成编码器/解码器。
选择序列化格式后,您可以开始考虑内存中格式,这在很大程度上取决于应用程序操作数据的方式,例如: