我编写了这些方法来将数据编码为TLV对象的数组,并将它们序列化和反序列化。如对改进等方面有任何反馈意见,将不胜感激。
请注意,到目前为止,我忽略了endianness问题,因为它们与我无关。另外,对于TLV对象,我使用的是固定大小的数组,在这个阶段,这对我来说应该很好。
ps。类似地,我还计划创建像add_int16、add_uint32等函数--可能以后我必须在printf中使用相应的参数,以避免UB?
//
// 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
// 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工作正常的
//
// 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;
}发布于 2014-07-05 19:11:51
就我个人而言,我不喜欢每次使用用户定义的类型时都编写struct:
struct tlv xyz;我建议您在结构化类型中使用typedef:
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,以强制执行此约束并避免意外更改函数中的参数:
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长度);
将其改写为:
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添加到bytes和src中。
在tlv_chain.c中有几行类似于此的行:
如果(dest->使用== 50)返回-1;
50是tlv_chain的object[]数组的大小。用名为#define或enum的常量替换这些原始常量:
#define MAX_TLV_OBJECTS 50与返回0或-1不同,更好的方法是使用一些错误代码常量:
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替换函数的返回类型:
tlv_result_t tlv_chain_print(struct tlv_chain *a);在职能方面:
int32_t tlv_chain_print(struct tlv_chain *a);
使用printf转储TLV对象。允许用户提供自己的打印机功能,甚至提供指向FILE (流)对象的指针(可以是STDOUT或用户文件),这可能很有趣。例如:
int32_t tlv_chain_print(struct tlv_chain *a, void(*printer)(const char *))或
int32_t tlv_chain_print(struct tlv_chain *a, FILE *dest_stream)https://codereview.stackexchange.com/questions/56203
复制相似问题