一、写在前面的话
parcel是Android中的Framework层的一个数据结构,以c++实现,可以理解为数据集或者数据容器,在android将其作为函数的参数以及binder通信等,这里是仿照Android的parcel写的一个c语言版本的parcel,当然并不是完全将Android的parcel翻译成c语言,还会对其进行精简,将一些复杂的东西统统丢掉,只留下,数据容器操作相关的部分。以便以后在各种通信(网络,线程间,硬件接口如SPI之间),数据封装等等情况下,可以快速使用。
二、接口定义
也就是一个header file。
#ifndef __TINY_PARCEL_H__
#define __TINY_PARCEL_H__
#include "general_type.h"
#include "general_error.h"
/***************************************************************************
*
* macro declaration
*
***************************************************************************/
/***************************************************************************
*
* data structure declaration
*
***************************************************************************/
/** @brief parcel data and interface */
typedef struct tiny_parcel_s
{
GU08* data; /**< pointer to data buffer */
GU32 pos; /**< offset position of parcel operation */
GU32 size; /**< the size of data buffer */
/**
*@brief write data to parcel
*
*@param parcel [in] pointer to parcel data structure.
*@param data [in] pointer to data buffer.
*@param len [in] the length of data
*
*@retuen success: G_OK, fail: error code.
*@see
*/
GS32(*write)(struct tiny_parcel_s* parcel, const void* data, GU32 len);
/**
*@brief write a 32 bits integer value to parcel
*
*@param parcel [in] pointer to parcel data structure.
*@param value [in] the value of integer
*
*@retuen success: G_OK, fail: error code.
*@see
*/
GS32 (*writeInt32)(struct tiny_parcel_s* parcel, GS32 value);
/**
*@brief write a 64 bits integer value to parcel
*
*@param parcel [in] pointer to parcel data structure.
*@param value [in] the value of integer
*
*@retuen success: G_OK, fail: error code.
*@see
*/
GS32(*writeInt64)(struct tiny_parcel_s* parcel, GS64 value);
/**
*@brief write a float value to parcel
*
*@param parcel [in] pointer to parcel data structure.
*@param value [in] the value of float
*
*@retuen success: G_OK, fail: error code.
*@see
*/
GS32(*writeFloat)(struct tiny_parcel_s* parcel, GFLT value);
/**
*@brief write a string to parcel
*
*@param parcel [in] pointer to parcel data structure.
*@param value [in] pointer to string
*
*@retuen success: G_OK, fail: error code.
*@see
*
*@note the string must end with '\0'.
*
*/
GS32(*writeString)(struct tiny_parcel_s* parcel, const char* value);
/**
*@brief read data from parcel
*
*@param parcel [in] pointer to parcel data structure.
*@param out_data [out] pointer to out data buffer.
*@param len [in] the length of out data(request read)
*
*@retuen success: G_OK, fail: error code.
*@see
*/
GS32(*read)(struct tiny_parcel_s* parcel, void* out_data, GU32 len);
/**
*@brief read a 32 bits integer from parcel
*
*@param parcel [in] pointer to parcel data structure.
*
*@retuen the value of integer
*@see
*/
GS32(*readInt32)(struct tiny_parcel_s* parcel);
/**
*@brief read a 64 bits integer from parcel
*
*@param parcel [in] pointer to parcel data structure.
*
*@retuen the value of integer
*@see
*/
GS64(*readInt64)(struct tiny_parcel_s* parcel);
/**
*@brief read a float from parcel
*
*@param parcel [in] pointer to parcel data structure.
*
*@retuen the value of float
*@see
*/
GFLT(*readFloat)(struct tiny_parcel_s* parcel);
/**
*@brief read a string from parcel
*
*@param parcel [in] pointer to parcel data structure.
*
*@retuen success: the pointer to string, fail: NULL
*@see
*/
GSTR(*readString)(struct tiny_parcel_s* parcel);
}tiny_parcel_t;
/***************************************************************************
*
* API declaration
*
***************************************************************************/
/**
*@brief initialize parcel data structure
*
*@param parcel [io] pointer to parcel data structure.
*@param data [in] pointer to data buffer.
*@param len [in] the length of out data()
*
*@retuen success: G_OK, fail: error code.
*@see
*
*@note this interface set does not thread safe
*
*/
GS32 TinyParcelInitialize(tiny_parcel_t* parcel, void* data, GU32 len);
#endif //end of __TINY_PARCEL_H__
其中general_xxx.h 就是前一章说的一些通用的头文件,其中定义基本数据类型,以及 功能 宏,通用错误号等等。 当然,这里也可以自己在这个parcel的header file中定义,但是因为c印记 这一系列的文章,绝大多数都会使用到这些基本数据类型以及通用错误号等东西,所以就特意将这些聚拢成几个公用的header file。
三、pracel功能的具体实现
#include <stdio.h>
#include <string.h> /** 标准c相关头文件 */
#include "tiny_parcel.h" /** parcel 接口声明头文件 */
/***************************************************************************
*
* macro define
*
***************************************************************************/
/** 宏定义一些log输出接口, 移植的时候只需要修改这个宏就可以改变log的输出方式 */
#define LOGE printf
#define LOGD printf
/***************************************************************************
*
* data structure define
*
***************************************************************************/
/***************************************************************************
*
* inner API define
*
***************************************************************************/
static inline GS32 TinyPracelWriteCommon(char* dst, GU32 dst_size, GU32* pos, const void* data, GU32 len)
{
GS32 ret = G_ErrorBadParameter;
if (data && (len > 0))
{
if ((*pos + len) <= dst_size)
{
memcpy(dst + *pos, data, len);
*pos += len;
ret = G_OK;
}
else
{
LOGE("doesn't enough data or space(pos+len(%d) < size(%d)) \n", (*pos + len), dst_size);
ret = G_ErrorOverflow;
}
}
return ret;
}
static inline GS32 TinyPracelWrite(tiny_parcel_t* parcel, const void* data, GU32 len)
{
return TinyPracelWriteCommon(parcel->data, parcel->size, &(parcel->pos), data, len);
}
static GS32 TinyPracelWriteInt32(tiny_parcel_t* parcel, GS32 value)
{
return TinyPracelWrite(parcel, (const void*)&value, sizeof(value));
}
static GS32 TinyPracelWriteInt64(tiny_parcel_t* parcel, GS64 value)
{
return TinyPracelWrite(parcel, (const void*)&value, sizeof(value));
}
static GS32 TinyPracelWriteFloat(tiny_parcel_t* parcel, GFLT value)
{
return TinyPracelWrite(parcel, (const void*)&value, sizeof(value));
}
static GS32 TinyPracelWriteString(tiny_parcel_t* parcel, const char* value)
{
return TinyPracelWrite(parcel, (const void*)value, value ? (strlen(value) + 1) : 0);
}
static inline GS32 TinyPracelRead(tiny_parcel_t* parcel, void* out_data, GU32 len)
{
return TinyPracelWriteCommon(out_data, parcel->size, &(parcel->pos), parcel->data, len);
}
static GS32 TinyPracelReadInt32(tiny_parcel_t* parcel)
{
GS32 value = 0;
TinyPracelRead(parcel, (void*)&value, sizeof(value));
return value;
}
static GS64 TinyPracelReadInt64(tiny_parcel_t* parcel)
{
GS64 value = 0;
TinyPracelRead(parcel, (void*)&value, sizeof(value));
return value;
}
static GFLT TinyPracelReadFloat(tiny_parcel_t* parcel)
{
GFLT value = 0;
TinyPracelRead(parcel, (void*)&value, sizeof(value));
return value;
}
static GSTR TinyPracelReadString(tiny_parcel_t* parcel)
{
GSTR value = NULL;
GU32 avail = parcel->size - parcel->pos;
if (avail > 0)
{
GSTR str = (GSTR)(parcel->data + parcel->pos);
// is the string's trailing NUL within the parcel's valid bounds?
GSTR eos = memchr(str, '\0', avail);
if (eos)
{
GU32 len = eos - str;
value = str;
parcel->pos += (len + 1); /** + 1, indicate skip '\0' */
}
}
return value;
}
/***************************************************************************
*
* API define
*
***************************************************************************/
GS32 TinyPracelInitialize(tiny_parcel_t* parcel, void* data, GU32 len)
{
GS32 ret = G_ErrorBadParameter;
if (parcel && data && (len > 0))
{
parcel->data = data;
parcel->size = len;
parcel->pos = 0;
/** register inteface */
parcel->read = TinyPracelRead;
parcel->readFloat = TinyPracelReadFloat;
parcel->readInt32 = TinyPracelReadInt32;
parcel->readInt64 = TinyPracelReadInt64;
parcel->readString = TinyPracelReadString;
parcel->write = TinyPracelWrite;
parcel->writeFloat = TinyPracelWriteFloat;
parcel->writeInt32 = TinyPracelWriteInt32;
parcel->writeInt64 = TinyPracelWriteInt64;
parcel->writeString = TinyPracelWriteString;
ret = G_OK;
}
return ret;
}
四、使用例子
1、 用途
parcel可以用作函数的参数,以支持各种参数组合以及后续扩展,就比如Android中AwesomePlayer中的invoke接口(其原型为:status_t invoke(const Parcel &request, Parcel *reply);)
parcel可以用来实现协议通信,比如模组与模组之间,线程与线程之间,进程与进程之间,甚至是系统与系统之间也可以使用parcel将需要传输的数据或信息按照自定义的格式(就相当于约定好的协议)组合写到一个buffer之中,然后通过网络,SPI,uart,I2C等方式传输到目标系统,再使用parcel的read接口,按照自定义的格式一一读取出来
2、例子
#include <stdio.h>
#include <string.h>
#include "tiny_parcel.h"
/***************************************************************************
*
* macro define
*
***************************************************************************/
#define LOGE printf
#define LOGD printf
/***************************************************************************
*
* API define
*
***************************************************************************/
int main(int argc, char* argv[])
{
#define PARCEL_BUF_SIZE 1024
char temp_buf[PARCEL_BUF_SIZE];
char* pbuf1 = temp_buf;
char* pbuf2 = NULL;
tiny_parcel_t write_parcel;
GS32 dataInt32 = 123;
GS64 dataInt64 = 456;
GFLT dataFloat = 789.123;
GSTR dataString = "this is string";
GSTR dataData = "this is data"; /** fake pure data */
GU32 dataLen = strlen(dataData);
GS32 ret = TinyParcelInitialize(&write_parcel, pbuf1, PARCEL_BUF_SIZE);
if (ret == G_OK)
{
LOGD("write data:\n");
LOGD("int32: %d\n", dataInt32);
LOGD("int64: %lld\n", dataInt64);
LOGD("float: %f\n", dataFloat);
LOGD("string: %s\n", dataString);
LOGD("pure data: %s\n", dataData);
/** write data to parcel */
write_parcel.writeInt32(&write_parcel, dataInt32);
write_parcel.writeInt64(&write_parcel, dataInt64);
write_parcel.writeFloat(&write_parcel, dataFloat);
write_parcel.writeString(&write_parcel, dataString);
ret = write_parcel.write(&write_parcel,dataData, dataLen);
if (ret == G_OK)
{
/** fake data transform */
pbuf2 = pbuf1;
LOGD("\n=======================================\n");
/** read data from parcel */
{/** fake other thread/porcessor/system */
GS32 readInt32 = 0;
GS64 readInt64 = 0;
GFLT readFloat = 0.0;
GSTR readString = NULL;
GU32 readDataLen = dataLen;
char readPureData[PARCEL_BUF_SIZE] = {0};
tiny_parcel_t read_parcel;
ret = TinyParcelInitialize(&read_parcel, pbuf2, PARCEL_BUF_SIZE);
if (ret == G_OK)
{
readInt32 = read_parcel.readInt32(&read_parcel);
readInt64 = read_parcel.readInt64(&read_parcel);
readFloat = read_parcel.readFloat(&read_parcel);
readString = read_parcel.readString(&read_parcel);
ret = read_parcel.read(&read_parcel, readPureData, readDataLen);
if (ret == G_OK)
{
LOGD("read data:\n");
LOGD("int32: %d\n", readInt32);
LOGD("int64: %lld\n", readInt64);
LOGD("float: %f\n", readFloat);
LOGD("string: %s\n", readString);
LOGD("pure data: %s\n", readPureData);
LOGD("\n=========================================\n");
if ((dataInt32 == readInt32) &&
(dataInt64 == readInt64) &&
(dataFloat == readFloat) &&
(!strcmp(dataString, readString)) &&
(!memcmp(dataData, readPureData, dataLen)))
{
LOGD("data check OK ...\n");
}
else
{
LOGD("data check failed ...\n");
}
}
else
{
LOGE("read pure data from parcel failed(0x%08x)\n", ret);
}
}
else
{
LOGE("initialize read parcel failed(0x%08x)\n", ret);
}
}
}
else
{
LOGE("read pure data to parcel failed(0x%08x)\n", ret);
}
}
else
{
LOGE("initialize write parcel failed(0x%08x)\n", ret);
}
return 0;
}