昨天我开始了解使用TLV格式表示信息。
如果您要使用 ANSI C 编写便携式 BER TLV 编码器/解码器,您会使用什么数据结构 (*)?
像下面这样的事情会做吗?
struct TlvElement
{
int nTag;
int nLength;
unsigned char *pValue; // Byte array
TlvElement *pNext;
};
(*) 不幸的是,我不能为此使用 C++ 和 STL。
昨天我开始了解使用TLV格式表示信息。
如果您要使用 ANSI C 编写便携式 BER TLV 编码器/解码器,您会使用什么数据结构 (*)?
像下面这样的事情会做吗?
struct TlvElement
{
int nTag;
int nLength;
unsigned char *pValue; // Byte array
TlvElement *pNext;
};
(*) 不幸的是,我不能为此使用 C++ 和 STL。
来自维基文章:
类型和长度大小固定(通常为 1-4 个字节)
所以,我会将nTag
and更改nLength
为一些固定长度的类型。int
的大小是特定于平台的,这可能会给您带来一些麻烦。为您的协议修复它们的大小并使用int8_t
, int16_t
orint32_t
等。对于nLength
,您甚至可以使用未签名的。
由于值可以是任何类型,我会使用void*
for 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
注意:再一次,这不是经过测试的代码,但我想你明白了。希望有帮助。
如果我要使用 ANSI C 编写 TLV 编码器/解码器,我会选择经过验证的、标准化的、灵活的数据序列化(即在线)格式:ASN.1 BER、Thrift等。
这是一个经典的领域,每天都会对车轮进行重新发明。聪明人已经想到了高效、可维护和灵活的解决方案;再次经历同样的过程没有什么意义。
例如,如果您的示例中的结构用于serialization,您仍然需要考虑:
int
取决于编译器平台和操作系统)一些现有的格式提供了语义和句法之间的分离;其他允许您为数据方案自动生成编码器/解码器。
一旦您选择了序列化格式,您就可以开始考虑内存格式,这在很大程度上取决于您的应用程序将如何操作数据,例如: