欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

c语言序列化和反序列化

程序员文章站 2022-06-16 08:38:44
...

c语言序列化和反序列化

网络调用,数据存取,数据传输都需要把数据序列化和反序列化。杀鸡不喜欢用牛刀,自己从底层设计协议又太繁琐,难以维护和扩展。使用 tpl (http://troydhanson.github.io/tpl/)这个库,可以很方便地构造自己的协议。

我采用 TLV 协议形式,即 (key,type,length,value) 4元组。key 是唯一的名称,type 是key保存的值的类型(用一个字符表示),length 是 value 的长度(应该叫 size 更贴近),value 是可以保存任何数据。发送数据的一方发送【“name”=“cheungmine”, “country”=“china”】, 接收数据的一方接收到之后,成为一个数组(UTArray),可以转为map(kvpairs_to_map),这样就容易获取特定些字段的内容了。

本文不特定于用户的具体协议内容,也不考虑数据存放的字节次序。这些都由用户自己完成。

因此可以定义个 tlv 数据节如下:

typedef struct kvpair_t
{
	char *key;        /* we'll use this field as the key */
    char type;

	union {
		struct {
			void *val;
			uint32_t siz;
		};

		tpl_bin bval;
	};
	
	UT_hash_handle hh; /* makes this structure hashable */
} kvpair_t;

一个数据祯由1~N个这样的TLV 构成。我实现了基本的序列化(pack)和反序列化(unpack)基础功能。任何协议只要在这基础封装自己特有的字段(key)即可。下面是 tplut.h和tplut.c以及测试代码。
下面的代码可以在任何平台编译使用。

tplut.h

/***********************************************************************
* Copyright (c) 2008-2080 pepstack.com, [email protected]
*
* ALL RIGHTS RESERVED.
* 
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 
*   Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
* 
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
***********************************************************************/
/**
 * @file: tplut.h
 *    tpl protocol utility
 *
 * @author: [email protected]
 *
 * @version: 1.0.0
 * @create: 2019-05-02
 * @update: 2019-05-03
 */
#ifndef TPLUT_H_INCLUDED
#define TPLUT_H_INCLUDED

#if defined(__cplusplus)
extern "C"
{
#endif

#include <stdio.h>
#include <stdlib.h>

#include <uthash/utarray.h>

#include <uthash/uthash.h>

/* http://troydhanson.github.io/tpl/userguide.html */
#include <tpl/tpl.h>

#define TPLUT_SUCCESS   0
#define TPLUT_ERROR   (-1)


typedef struct kvpair_t * kvmap_t;


/**
 * S(sc)B
 * 
 * A(S(sc)B)
 */
typedef struct kvpair_t
{
	char *key;        /* we'll use this field as the key */
    char type;

	union {
		struct {
			void *val;
			uint32_t siz;
		};

		tpl_bin bval;
	};
	
	UT_hash_handle hh; /* makes this structure hashable */
} kvpair_t;


/**
 * kvpair_t and tpl api
 */

extern UT_array * kvpairs_new (int capacity);

extern void kvpairs_free (UT_array *pairs);

extern void kvpairs_add (UT_array *pairs, char *key, char type, void *value, uint32_t size);

extern void kvpairs_print (UT_array *pairs);

extern tpl_node * kvpairs_pack (UT_array *pairs);

extern int dump_bin (tpl_node *tn, tpl_bin *outbin);

extern tpl_node * load_bin (tpl_bin *inbin);

extern UT_array * kvpairs_unpack (tpl_bin *tbin, int capacity);


/**
 * kvmap_t api
 */
extern void kvmap_init (kvmap_t *map);

extern void kvmap_uninit (kvmap_t *map);

extern void kvpairs_to_map (UT_array *pairs, kvmap_t *map);

extern void kvmap_add (kvmap_t * map, kvpair_t * kv);

extern kvpair_t * kvmap_find (kvmap_t * map, const char * key);

extern void kvmap_delete (kvmap_t * map, kvpair_t *kv);

extern void kvmap_clear (kvmap_t * map);


#if defined(__cplusplus)
}
#endif

#endif /* TPLUT_H_INCLUDED */

tplut.c

/***********************************************************************
* Copyright (c) 2008-2080 pepstack.com, [email protected]
*
* ALL RIGHTS RESERVED.
* 
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 
*   Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
* 
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
***********************************************************************/
/**
 * @file: tplut.c
 *    tpl protocol utility
 *
 * @author: [email protected]
 *
 * @version: 1.0.0
 * @create: 2019-05-02
 * @update: 2019-05-03
 */
#include "tplut.h"

/**
 * http://troydhanson.github.io/tpl/userguide.html
 * 
 * Type 	Description 	             Required argument type
 * -----------------------------------------------------------------
 *  j     16-bit signed int           int16_t* or equivalent
 *  v     16-bit unsigned int         uint16_t* or equivalent
 *  i     32-bit signed int           int32_t* or equivalent
 *  u     32-bit unsigned int         uint32_t* or equivalent
 *  I     64-bit signed int           int64_t* or equivalent
 *  U     64-bit unsigned int         uint64_t* or equivalent
 *  c     character (byte)            char*
 *  s     string                      char**
 *  f     64-bit double precision     double* (varies by platform)
 *  B     binary buffer (arbitrary-length)
 */

#include <pthread.h>


static void kvpair_copy(void *_dst, const void *_src)
{
	kvpair_t *dst = (kvpair_t *)_dst, *src = (kvpair_t *)_src;

	dst->key = (src->key ? strdup(src->key) : NULL);
	dst->type = src->type;

	if (src->val) {
		dst->val = malloc(src->siz);
		if (! dst->val) {
			oom();
		}
		dst->siz = src->siz;
		memcpy(dst->val, src->val, src->siz);
	} else {
		dst->val = NULL;
		dst->siz = 0;
	}	
}


static void kvpair_dtor(void *_elt)
{
    kvpair_t *elt = (kvpair_t *)_elt;

    if (elt->key) {
		free(elt->key);
	}

    if (elt->val) {
		free(elt->val);
	}
}

static const UT_icd ut_kvpair_icd UTARRAY_UNUSED = { sizeof(kvpair_t), NULL, kvpair_copy, kvpair_dtor };


/**
 * kvpair_t and tpl api
 */

UT_array * kvpairs_new (int capacity)
{
	UT_array *pairs;

	utarray_new(pairs, &ut_kvpair_icd);
	utarray_reserve(pairs, capacity);

	return pairs;
}


void kvpairs_free (UT_array *pairs)
{
	utarray_free(pairs);
}


void kvpairs_add (UT_array *pairs, char *key, char type, void *value, uint32_t size)
{
	kvpair_t elt = { key, type, value, size };

	utarray_push_back(pairs, &elt);
}


void kvpairs_print (UT_array *pairs)
{
	kvpair_t *p = NULL;

	while ((p = (kvpair_t *) utarray_next(pairs, p))) {
		printf("%s = %.*s\n", p->key, (int) p->siz, (char *) p->val);
	}
}


void kvpairs_to_map (UT_array *pairs, kvmap_t *map)
{
	kvpair_t *p = NULL;

	while ((p = (kvpair_t *) utarray_next(pairs, p))) {
		kvmap_add(map, p);
	}
}


tpl_node * kvpairs_pack (UT_array *pairs)
{
	tpl_node *tn;
    
    kvpair_t kv, *p;

	tn = tpl_map("A(S(sc)B)", &kv, &kv.bval);
    if (! tn) {
        oom();
    }

	p = NULL;
	while ((p = (kvpair_t *) utarray_next(pairs, p))) {
		kv = *p;

		tpl_pack(tn, 1);
	}

	return tn;
}


int dump_bin (tpl_node *tn, tpl_bin *tb)
{
	if (tpl_dump(tn, TPL_GETSIZE, &tb->sz) == 0) {
		tb->addr = malloc(tb->sz);

		if (! tb->addr) {
			oom();
		}

        if (tpl_dump(tn, TPL_MEM | TPL_PREALLOCD, tb->addr, tb->sz) == 0) {
			return 0;
        }

		free(tb->addr);
    }

	return (-1);
}


tpl_node * load_bin (tpl_bin *tbin)
{
	tpl_node *tn;
    
    kvpair_t kv;

	tn = tpl_map("A(S(sc)B)", &kv, &kv.bval);
    if (! tn) {
        oom();
    }

	if (tpl_load(tn, TPL_MEM /* | TPL_EXCESS_OK */, tbin->addr, tbin->sz) != 0) {
		tpl_free(tn);
		return NULL;
	}

	return tn;
}


UT_array * kvpairs_unpack (tpl_bin *tbin, int capacity)
{
	UT_array *pairs;

	tpl_node *tn;
    kvpair_t kv;

	pairs = kvpairs_new(capacity);

	tn = tpl_map("A(S(sc)B)", &kv, &kv.bval);
    if (! tn) {
		kvpairs_free(pairs);
        oom();
        return NULL;
    }

	if (tpl_load(tn, TPL_MEM /* | TPL_EXCESS_OK */, tbin->addr, tbin->sz) != 0) {
		tpl_free(tn);
		kvpairs_free(pairs);
		return NULL;
	}
	
	while (tpl_unpack(tn, 1) > 0) {
		kvpairs_add(pairs, kv.key, kv.type, kv.val, kv.siz);
		kvpair_dtor((void*) &kv);
	}

	tpl_free(tn);

	return pairs;
}


/**
 * kvmap_t api
 */

void kvmap_init (kvmap_t * map)
{
	*map = NULL;
}


void kvmap_uninit (kvmap_t * map)
{
	HASH_CLEAR(hh, *map);
	*map = NULL;
}


void kvmap_add (kvmap_t * map, kvpair_t * kv)
{
    HASH_ADD_STR(*map, key, kv);
}


kvpair_t * kvmap_find (kvmap_t * map, const char * key)
{
    kvpair_t *kv = 0;

    HASH_FIND_STR(*map, key, kv);

    return kv;
}


void kvmap_delete (kvmap_t * map, kvpair_t *kv)
{
    HASH_DEL(*map, kv);
}


void kvmap_clear (kvmap_t * map)
{
    HASH_CLEAR(hh, *map);
}

测试代码

/**
 * @file: test.c
 *    tpl transfer protocol test
 *
 * @author: [email protected]
 *
 * @version: 1.0.0
 * @create: 2019-05-02
 * @update: 2019-05-02
 */
#include "tplut.h"


void test_tplut (int id, int num)
{
	int len;
	char key[30];
	char val[30];
	UT_array * pairs;
	tpl_bin tb;

	int capacity = num;

	pairs = kvpairs_new(capacity);

	while (num-- > 0) {
		snprintf(key, sizeof key, "(%d) key:%d", id, num);
		len = snprintf(val, sizeof val, "(%d) val:%d", id, num);
		kvpairs_add(pairs, key, 's', val, len + 1);
	}

	kvpairs_print(pairs);

	printf("kvpairs_pack.\n");
	tpl_node *tn = kvpairs_pack(pairs);

	printf("kvpairs_free.\n");
	kvpairs_free(pairs);

	printf("dump_bin.\n");
	if (dump_bin(tn, &tb) == 0) {
		printf("dump_bin ok: sz=%u bytes.\n", tb.sz);
		
		printf("load_bin\n");
		tpl_node *tn2 = load_bin(&tb);
		if (tn2) {
			printf("load_bin ok.\n");
			tpl_free(tn2);
		}

		UT_array * kvs = kvpairs_unpack(&tb, capacity);
		if (kvs) {
			printf("kvpairs_unpack out ok:\n");
			kvpairs_print(kvs);

			kvmap_t kvmap;
			kvmap_init(&kvmap);

			kvpairs_to_map(kvs, &kvmap);

			kvmap_uninit(&kvmap);

			kvpairs_free(kvs);
		}

		free(tb.addr);
	}

	printf("tpl_free.\n");
	tpl_free(tn);
}


int main (int argc, char *argv[])
{
	int i;

	for (i = 0; i < 1000000; i++) {
		test_tplut(i, 30);
	}

	printf("all is ok.\n");

	return (0);
}

参考

https://github.com/troydhanson/tpl