首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >类型长度值(TLV)编码/解码

类型长度值(TLV)编码/解码
EN

Code Review用户
提问于 2014-07-05 15:35:50
回答 1查看 19.6K关注 0票数 12

我编写了这些方法来将数据编码为TLV对象的数组,并将它们序列化和反序列化。如对改进等方面有任何反馈意见,将不胜感激。

请注意,到目前为止,我忽略了endianness问题,因为它们与我无关。另外,对于TLV对象,我使用的是固定大小的数组,在这个阶段,这对我来说应该很好。

ps。类似地,我还计划创建像add_int16、add_uint32等函数--可能以后我必须在printf中使用相应的参数,以避免UB?

tlv_chain.c

代码语言:javascript
复制
//
//  tlv_chain.c
//  tlv
//
//  Created by macbook air on 1/17/12.
//  Copyright (c) 2012 macbook air. All rights reserved.
//


#include "tlv_chain.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int32_t tlv_chain_add_int32(struct tlv_chain *a, int32_t x)
{
    return tlv_chain_add_raw(a, 1, 4, &x);
}

// add tlv object which contains null terminated string
int32_t tlv_chain_add_str(struct tlv_chain *a,  char *str)
{
    return tlv_chain_add_raw(a, 2, strlen(str) + 1, str);
}

int32_t tlv_chain_add_raw(struct tlv_chain *a, unsigned char type, int16_t size, unsigned char *bytes)
{
    if(a == NULL || bytes == NULL)
        return -1;

    // all elements used in chain?
    if(a->used == 50)
        return -1;

    int index = a->used;
    a->object[index].type = type;
    a->object[index].size = size;
    a->object[index].data = malloc(size);
    memcpy(a->object[index].data, bytes, size);

    // increase number of tlv objects used in this chain
    a->used++;

    // success
    return 0;
}

int32_t tlv_chain_free(struct tlv_chain *a)
{
    if(a == NULL)
        return -1;

    for(int i =0; i < a->used; i++)
    {
        free(a->object[i].data);

        a->object[i].data = NULL;
    }

    return 0;
}

// serialize the tlv chain into byte array
int32_t tlv_chain_serialize(struct tlv_chain *a, unsigned char *dest, /* out */ int32_t* count)
{
    if(a == NULL || dest == NULL)
        return -1;

    // Number of bytes serialized
    int32_t counter = 0;

    for(int i = 0; i < a->used; i++)
    {
        dest[counter] = a->object[i].type;
        counter++;

        memcpy(&dest[counter], &a->object[i].size, 2);
        counter += 2;

        memcpy(&dest[counter], a->object[i].data, a->object[i].size);
        counter += a->object[i].size;
    }

    // Return number of bytes serialized
    *count = counter;

    // success
    return 0;
}

int32_t tlv_chain_deserialize(unsigned char *src, struct tlv_chain *dest, int32_t length)
{
    if(dest == NULL || src == NULL)
        return -1;

    // we want an empty chain
    if(dest->used != 0)
        return -1;

    int32_t counter = 0;
    while(counter < length)
    {
        if(dest->used == 50)
            return -1;

        // deserialize type
        dest->object[dest->used].type = src[counter];
        counter++;

        // deserialize size
        memcpy(&dest->object[dest->used].size, &src[counter], 2);
        counter+=2;

        // deserialize data itself, only if data is not NULL
        if(dest->object[dest->used].size > 0)
        {
            dest->object[dest->used].data = malloc(dest->object[dest->used].size);
            memcpy(dest->object[dest->used].data, &src[counter], dest->object[dest->used].size);
            counter += dest->object[dest->used].size;
        }else
        {
            dest->object[dest->used].data = NULL;
        }

        // increase number of tlv objects reconstructed
        dest->used++;
    }

    // success
    return 0;

}



int32_t tlv_chain_print(struct tlv_chain *a)
{
    if(a == NULL)
        return -1;

    // go through each used tlv object in the chain
    for(int i =0; i < a->used; i++)
    {

        if(a->object[i].type == 1)
        {
            // int32
            int32_t x;
            memcpy(&x, a->object[i].data, sizeof(int32_t));
            printf("%d \n",x);

        }else if(a->object[i].type == 2)
        {
            // string
            printf("%s \n",a->object[i].data);
        }
    }


    return 0;
}

tlv_chain.h

代码语言:javascript
复制
//
//  tlv_chain.h
//  tlv
//
//  Created by macbook air on 1/17/12.
//  Copyright (c) 2012 macbook air. All rights reserved.
//

#ifndef tlv_tlv_chain_h
#define tlv_tlv_chain_h
#include <stdint.h>

// TLV data structure
struct tlv
{
    int8_t type;    // type
    uint8_t * data; // pointer to data
    int16_t size;   // size of data
};

// TLV chain data structure. Contains array of (50) tlv
// objects. 
struct tlv_chain
{
    struct tlv object[50];
    uint8_t used; // keep track of tlv elements used
};

int32_t tlv_chain_add_int32(struct tlv_chain *a, int32_t x);
int32_t tlv_chain_add_str(struct tlv_chain *a, char *str);
int32_t tlv_chain_add_raw(struct tlv_chain *a, unsigned char type, int16_t size, unsigned char *bytes);
int32_t tlv_chain_serialize(struct tlv_chain *a, unsigned char *dest, int32_t *count);
int32_t tlv_chain_deserialize(unsigned char *src, struct tlv_chain *dest, int32_t length);
int32_t tlv_chain_print(struct tlv_chain *a);
int32_t tlv_chain_free(struct tlv_chain *a);

#endif

工作正常的

测试程序:

代码语言:javascript
复制
//
//  main.c
//  tlv
//
//  Created by macbook air on 1/17/12.
//  Copyright (c) 2012 macbook air. All rights reserved.
//

#include <stdio.h>
#include "tlv_chain.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(int argc, const char * argv[])
{
    struct tlv_chain chain1, chain2;
    memset(&chain1, 0, sizeof(chain1));
    memset(&chain2, 0, sizeof(chain2));
    unsigned char chainbuff[2048] = {0};
    int32_t l = 0;

    tlv_chain_add_int32(&chain1, 31144);
    tlv_chain_add_str(&chain1, "george");
    tlv_chain_add_int32(&chain1, 7);
    tlv_chain_add_str(&chain1, "998967-44-33-44-12");
    tlv_chain_add_str(&chain1, "Grand Chamption Atlanta; details: Ave12");
    tlv_chain_add_int32(&chain1, 7900);

    // serialization/deserialization test
    tlv_chain_serialize(&chain1, chainbuff, &l);
    tlv_chain_deserialize(chainbuff, &chain2, l);

    // print the tlv chain contents
    tlv_chain_print(&chain2);

    // free each chain
    tlv_chain_free(&chain1);
    tlv_chain_free(&chain2);

    return 0;
}
EN

回答 1

Code Review用户

发布于 2014-07-05 19:11:51

类型--结构:

就我个人而言,我不喜欢每次使用用户定义的类型时都编写struct

代码语言:javascript
复制
struct tlv xyz;

我建议您在结构化类型中使用typedef

代码语言:javascript
复制
typedef struct tlv
{
    int8_t type;    // type
    uint8_t * data; // pointer to data
    int16_t size;   // size of data

} tlv_t;

typedef struct tlv_chain
{
    struct tlv object[50];
    uint8_t used; // keep track of tlv elements used

} tlv_chain_t;

这也将使您的代码用户可以选择他们最喜欢的方式。但是请注意,我在本例中使用的_t后缀是留待POSIX标准将来使用 (感谢@syb0rg指出这一点)。如果您的目标是与该标准完全兼容,那么最好避免使用常见的_t后缀。

一致使用const:

在职能方面:

int32_t tlv_chain_add_str(struct tlv_chain *a,char *str);

str参数是只读的(您是在测试中将char*文本传递给它),因此应该是const,以强制执行此约束并避免意外更改函数中的参数:

代码语言:javascript
复制
int32_t tlv_chain_add_str(struct tlv_chain *a, const char *str);
                                               ^^^^^

其他两种情况也是如此:

int32_t tlv_chain_add_raw(结构tlv_chain *a,无符号字符类型,int16_t大小,无符号字符*字节);int32_t tlv_chain_deserialize(无符号字符*src,struct tlv_chain *dest,int32_t长度);

将其改写为:

代码语言:javascript
复制
int32_t tlv_chain_add_raw(struct tlv_chain *a, unsigned char type, int16_t size, const unsigned char *bytes);
int32_t tlv_chain_deserialize(const unsigned char *src, struct tlv_chain *dest, int32_t length);

const添加到bytessrc中。

避免“魔术”数字:

tlv_chain.c中有几行类似于此的行:

如果(dest->使用== 50)返回-1;

50tlv_chainobject[]数组的大小。用名为#defineenum的常量替换这些原始常量:

代码语言:javascript
复制
#define MAX_TLV_OBJECTS 50

考虑从函数返回错误代码:

与返回0-1不同,更好的方法是使用一些错误代码常量:

代码语言:javascript
复制
typedef enum tlv_result {

    TLV_SUCESS,
    TLV_ERROR_1,
    TLV_ERROR_2,
    // etc...

} tlv_result_t;

当然,用错误描述替换TLV_ERROR_*常量中的数字。例如:TLV_ERROR_OUT_OF_MEMORY

然后用tlv_result_t替换函数的返回类型:

代码语言:javascript
复制
tlv_result_t tlv_chain_print(struct tlv_chain *a);

其他一般性建议:

在职能方面:

int32_t tlv_chain_print(struct tlv_chain *a);

使用printf转储TLV对象。允许用户提供自己的打印机功能,甚至提供指向FILE (流)对象的指针(可以是STDOUT或用户文件),这可能很有趣。例如:

代码语言:javascript
复制
int32_t tlv_chain_print(struct tlv_chain *a, void(*printer)(const char *))

代码语言:javascript
复制
int32_t tlv_chain_print(struct tlv_chain *a, FILE *dest_stream)
票数 10
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/56203

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档