4

昨天我开始了解使用TLV格式表示信息。

如果您要使用 ANSI C 编写便携式 BER TLV 编码器/解码器,您会使用什么数据结构 (*)?

像下面这样的事情会做吗?

struct TlvElement
{
    int nTag;
    int nLength;
    unsigned char *pValue; // Byte array
    TlvElement *pNext;
}; 

(*) 不幸的是,我不能为此使用 C++ 和 STL。

4

2 回答 2

3

来自维基文章:

类型和长度大小固定(通常为 1-4 个字节)

所以,我会将nTagand更改nLength为一些固定长度的类型。int的大小是特定于平台的,这可能会给您带来一些麻烦。为您的协议修复它们的大小并使用int8_t, int16_torint32_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

注意:再一次,这不是经过测试的代码,但我想你明白了。希望有帮助。

于 2012-08-11T08:55:46.433 回答
1

如果我要使用 ANSI C 编写 TLV 编码器/解码器,我会选择经过验证的、标准化的、灵活的数据序列化(即在线)格式:ASN.1 BERThrift等。

这是一个经典的领域,每天都会对车轮进行重新发明。聪明人已经想到了高效、可维护和灵活的解决方案;再次经历同样的过程没有什么意义。

例如,如果您的示例中的结构用于serialization,您仍然需要考虑:

  • 字节序问题
  • 语言类型的大小(大小int取决于编译器平台和操作系统)
  • 有效载荷中的数据类型(您可能想要携带原始数据、字符串、数字、位字段、枚举等)
  • 标签号的集中分配
  • 可选元素和选择
  • 复合结构(例如 TLV 列表)

一些现有的格式提供了语义和句法之间的分离;其他允许您为数据方案自动生成编码器/解码器。

一旦您选择了序列化格式,您就可以开始考虑内存格式,这在很大程度上取决于您的应用程序将如何操作数据,例如:

  • 应用程序如何在解码后提取数据(例如,给定一个整数项,应用程序是访问编码表示还是可以轻松使用的本机表示?)
  • 应用程序如何在编码前准备数据
  • 应用程序是否是多线程的
  • 是否要最小化复制开销(例如,如果您有大量原始数据,是否需要对其进行复制以对其进行编码?如果原始数据是碎片化的,您是否需要在连续内存中的某个地方对其进行重组以对其进行编码? )
  • 解码和解码是否可以增量完成
  • 分配的内存如何属于:应用程序还是库?
  • 您如何处理内存不足和未知标签等错误

我建议看一下由asn1c生成的用于处理 ASN.1 BER 的 API,或者查看libtasn1的 API 。

于 2012-08-11T09:44:29.007 回答