C++

1. 合约编译(C/C++版)

C/C++合约编译工具链在如下库:

https://github.com/bottos-project/contract-tool-cpp.git

这个工具用来编译C/C++合约,生成wasm/wast文件,以及扫描生成abi文件,下面以库里的两个例子来说明:

1. 例子testHelloWorld

一份合约代码放在一个文件夹里,文夹件放在“contract-tool-cpp”文件夹里,如testHelloWorld。

要编译合约时,先进入到合约的目录,如进入testHelloWorld目录,在该目录下使用如下命令来编译合约,这样就在同级目录下生成testHelloWorld.wast 和 testHelloWorld.wasm:

python ../gentool.py wasm testHelloWorld.cpp

由于这个例子里不涉及abi,所以我们在下面的例子再描述一下abi生成相关的内容。

2. 例子testRegUser

编译合约如同前面的例子,进入到testRegUser目录下用如下命令编译合约:

python ../gentool.py wasm testRegUser.cpp

我们重点来看一下合约abi文件的生成,首先简单介绍一下abi文件的组成:

  • structs :扫描出来的结构体的定义描述,供后面使用;
  • actions:合约提供的方法描述,其中action_name 是方法名, type 是调用合约所要的参数;
  • tables :合约持久化数据访问接口描述,其中table_name 是表名,index_type 是索引的类型, key_names 和key_types 分别是键值的名称和类型,type 是内容的结构体定义。

abi文件是通过扫描hpp文件生成, 在hpp文件里通过注释来告诉扫描器具体的定义:

  • "//@abi action reguser":

​ 定义了一个方法reguser,对应的入参定义为UserInfo;

  • "//@abi table userinfo:[index_type:string, "key_names:userName, key_types:string] ":

​ 定义了一个表, 表内容的结构体定义为UserBaseInfo。

在testRegUser文件夹下通过如下命令就可以扫描出hpp文件对应的abi文件:

python ../gentool testRegUser.hpp

2. 合约编写(C/C++版)

2.1. 一个最简单的合约

合约的入口函数是start函数,我们创建一个简单的合约,即合约被调用时打印一下"hello world in start”:

#include "contractcomm.hpp"

int start(char* method) 
{
    myprints("hello world in start");

    return 0;
}

其中"contractcomm.hpp"是一些常用的基本接口声明文件。

调用一下这个合约,将会在节点的log中打印"hello world in start":

2018-08-06 16:05:20 [INF] vm/wasm/exec/env_func.go:390 prints(): VM: func prints: hello world in start

2.2. 获取调用合约的方法

调用合约时,我们需要指定调用合约某一个具体的方法,即如下的“method”参数“test_method”:

{"version":1, "cursor_num":28,"cursor_label":3745260307,"lifetime":15270819998,"sender":"example", "contract":"example", "method":"test_method", "param":"", "sig_alg":1, "signature":""}

这个方法通过start函数的入参传入到合约里面,即下面的method参数,我们在合约里增加打印该参数的语句:

#include "contractcomm.hpp"

int start(char* method) 
{
    myprints("hello world in start");
    myprints(method);

    return 0;
}

我们构造一个交易,将method设置成一个特定的字符串,即下面的“test_method”:(这里省去了签名的具体值,下同)

{"version":1, "cursor_num":28,"cursor_label":3745260307,"lifetime":15270819998,"sender":"example", "contract":"example", "method":"test_method", "param":"", "sig_alg":1, "signature":""}

调用这个合约,将有下面的log输出:打印设置的"method"参数,即”test_method“:

2018-08-07 14:40:22 [INF] vm/wasm/exec/env_func.go:412 prints(): VM: func prints: hello word in start

2018-08-07 14:40:22 [INF] vm/wasm/exec/env_func.go:412 prints(): VM: func prints: test_method

我们可以在合约里根据method提供不同的合约实现,即方法,通过比较method参数进而走到不同的方法分支里,例如如下合约里提供的"add"、"del"方法:

#include "contractcomm.hpp"
#include "string.hpp"

int start(char* method) 
{
    myprints("hello word in start");
    myprints(method );

    if (0 == strcmp("add", method))
    {
        myprints("it is method add");
    }
    else if (0 == strcmp("del", method))
    {
        myprints("it is method del");
    }

    return 0;
}

2.3. 获取调用合约参数

调用合约时,除了需要指定调用合约的方法,同时也要带上对应的参数,即如下的"param"参数:

{"version":1, "cursor_num":28,"cursor_label":3745260307,"lifetime":15270819998,"sender":"example", "contract":"example", "method":"test_method", "param":"", "sig_alg":1, "signature":""}

合约里可以通过"getParam"接口来获取到该参数,例如:

#include "contractcomm.hpp"
#define ERROR_PARAM (-1)

int start(char* method) 
{
    char param[PARAM_MAX_LEN];
    uint32_t paramLen = 0;    

    paramLen = getParam(param, PARAM_MAX_LEN);

    if (0 == paramLen) 
    {
        myprints("paramLen is 0, error");
        return ERROR_PARAM;
    }

    myprints("paramLen is:");
    printi(paramLen);    
    myprints("param detail:");
    for(int i = 0;i<paramLen;i++)
    {
        printi(param[i]);
    }

    return 0;
}

我们构造一个交易:参数是经过MessagePack进行序列化的值,比如我们需要传一个序列化后为[97,98,99]的参数,将其序列化后的值转换化为16进制字符串:”616263“,然后填入param里:

{"version":1, "cursor_num":28,"cursor_label":3745260307,"lifetime":15270819998,"sender":"example", "contract":"example", "method":"test_method", "param":"616263", "sig_alg":1, "signature":""}

调用这个合约,将有下面的log输出,即可以在合约里面获取到了序列化后为[97,98,99]的值:

2018-08-08 17:25:26 [INF] vm/wasm/exec/env_func.go:431 prints(): VM: func prints: paramLen is:

2018-08-08 17:25:26 [INF] vm/wasm/exec/env_func.go:405 printi(): VM: from contract:example, method:test_method, func printi: 3

2018-08-08 17:25:26 [INF] vm/wasm/exec/env_func.go:431 prints(): VM: func prints: param detail:

2018-08-08 17:25:26 [INF] vm/wasm/exec/env_func.go:405 printi(): VM: from contract:example, method:test_method, func printi: 97

2018-08-08 17:25:26 [INF] vm/wasm/exec/env_func.go:405 printi(): VM: from contract:example, method:test_method, func printi: 98

2018-08-08 17:25:26 [INF] vm/wasm/exec/env_func.go:405 printi(): VM: from contract:example, method:test_method, func printi: 99

2.4. 合约数据的存储与读取

为了便于数据的统一访问,我们要求用MessagePack对合约要保存的数据进行对象序列化,相应的对读取到的数据进行反序列化从而得到原始的数据。

我们定义一个测试用的结构:

struct TestStruct {
    uint32_t valueA;
    uint32_t valueB;
};

我们通过param传入一个类型为TestStruct的参数,里面字段的值分别填1、2,首先对其序列化,得到[220,0,2,206,0,0,0,1,206,0,0,0,2],转化成16进制字符串:“dc0002ce00000001ce00000002”,我们将交易里的param里填入该值:

{"version":1, "cursor_num":28,"cursor_label":3745260307,"lifetime":15270819998,"sender":"example", "contract":"example", "method":"test_method", "param":"dc0002ce00000001ce00000002", "sig_alg":1, "signature":""}

我们在合约里将传入的参数进行反序化:

#include "contractcomm.hpp"
#define ERROR_PARAM (-1)
#define ERROR_UNPACK (-2)

struct TestStruct {
    uint32_t valueA;
    uint32_t valueB;
};


static bool unpack_struct(MsgPackCtx *ctx, TestStruct *info)
{    
    uint32_t size = 0;

    if (!unpack_array(ctx, &size)) return false; 
    if (2 != size) return false;

    if (!unpack_u32(ctx, &info->valueA)) return false; 
    if (!unpack_u32(ctx, &info->valueB)) return false; 

    return true;
}

static bool pack_struct(MsgPackCtx *ctx, TestStruct *info)
{    
    if (!pack_array16(ctx, 2)) return false;

    if (!pack_u32(ctx, info->valueA)) return false; 
    if (!pack_u32(ctx, info->valueB)) return false; 

    return true;
}

int start(char* method) 
{
    char param[PARAM_MAX_LEN];
    uint32_t paramLen = 0;    

    paramLen = getParam(param, PARAM_MAX_LEN);

    if (0 == paramLen) 
    {
        myprints("paramLen is 0, error");
        return ERROR_PARAM;
    }

    MsgPackCtx ctx;
    msgpack_init(&ctx, (char*)param, paramLen);

    TestStruct testStruct;
    bool suc = unpack_struct(&ctx, &testStruct);
    if (!suc)
    {
        myprints("unpack struct error");        
        return ERROR_UNPACK;
    }

    myprints("data from input param:");
    printi(testStruct.valueA);
    printi(testStruct.valueB);

    return 0;
}

调用这个合约,我们可以看到里面的这个结构体正确的获取到调用时所设置的参数,分别为1、2:

2018-08-08 18:20:00 [INF] vm/wasm/exec/env_func.go:431 prints(): VM: func prints: data from input param:

2018-08-08 18:20:00 [INF] vm/wasm/exec/env_func.go:405 printi(): VM: from contract:example, method:test_method, func printi: 1

2018-08-08 18:20:00 [INF] vm/wasm/exec/env_func.go:405 printi(): VM: from contract:example, method:test_method, func printi: 2

我们将这份数据改写后通过setBinValue接口保存起来,然后再通过getBinValue将数据读取出来,再进行反序列化得到真实数据:

#include "contractcomm.hpp"
#define ERROR_PARAM (-1)
#define ERROR_UNPACK (-2)
#define ERROR_SAVE_DB (-3)
#define ERROR_READ_DB (-4)
#define ERROR_CONTRACT_NAME (-5)

struct TestStruct {
    uint32_t valueA;
    uint32_t valueB;
};

static bool unpack_struct(MsgPackCtx *ctx, TestStruct *info)
{    
    uint32_t size = 0;

    if (!unpack_array(ctx, &size)) return false; 
    if (2 != size) return false;

    if (!unpack_u32(ctx, &info->valueA)) return false; 
    if (!unpack_u32(ctx, &info->valueB)) return false; 

    return true;
}

static bool pack_struct(MsgPackCtx *ctx, TestStruct *info)
{    
    if (!pack_array16(ctx, 2)) return false;

    if (!pack_u32(ctx, info->valueA)) return false; 
    if (!pack_u32(ctx, info->valueB)) return false; 

    return true;
}

int start(char* method) 
{
    char param[PARAM_MAX_LEN];
    uint32_t paramLen = 0;    

    paramLen = getParam(param, PARAM_MAX_LEN);
    if (0 == paramLen) 
    {
        myprints("paramLen is 0, error");
        return ERROR_PARAM;
    }

    /* 将param反序化 */
    MsgPackCtx ctx;
    msgpack_init(&ctx, (char*)param, paramLen);

    TestStruct testStruct;
    bool suc = unpack_struct(&ctx, &testStruct);
    if (!suc)
    {
        myprints("unpack struct error");

        return ERROR_UNPACK;
    }

    myprints("data from input param:");
    printi(testStruct.valueA);
    printi(testStruct.valueB);

    /* 将字段改写成3、4 */
    testStruct.valueA = 3;
    testStruct.valueB = 4;

    /* 序列化操作 */
    msgpack_init(&ctx, (char*)param, PARAM_MAX_LEN);
    suc = pack_struct(&ctx, &testStruct);
    if (!suc)
    {
        myprints("pack struct error");        
        return ERROR_UNPACK;
    }

    char tableName[] = "testTableName";
    char keyName[] = "testKeyName";

    /* 调用接口保存起来 */
    uint32_t handleResult = setBinValue(tableName, strlen(tableName), keyName, strlen(keyName), ctx.buf, ctx.pos);
    if (0 == handleResult)
    {
        myprints("save to db error");        
        return ERROR_SAVE_DB;
    }

    char contractname[STR_ARRAY_LEN(USER_NAME_MAX_LEN)];   
    uint32_t contractNameLen = getCtxName(contractname, STR_ARRAY_LEN(USER_NAME_MAX_LEN));
    if (0 == contractNameLen)
    {
        myprints("contract name error");        
        return ERROR_CONTRACT_NAME;
    }

    /* 调用接口获取到刚保存的值 */
    handleResult = getBinValue(contractname, contractNameLen, tableName, strlen(tableName), keyName, strlen(keyName), param, PARAM_MAX_LEN);
    if (0 == handleResult)
    {
        myprints("read from db error");        
        return ERROR_READ_DB;
    }

    /* 反序列化操作 */
    msgpack_init(&ctx, (char*)param, handleResult);
    suc = unpack_struct(&ctx, &testStruct);
    if (!suc)
    {
        myprints("unpack struct error");        
        return ERROR_UNPACK;
    }

    myprints("data from db:");
    printi(testStruct.valueA);
    printi(testStruct.valueB);   

    return 0;
}

用如下的交易(即输入结构体,字段分别为1、2)再次调用这个合约:

{"version":1, "cursor_num":28,"cursor_label":3745260307,"lifetime":15270819998,"sender":"example", "contract":"example", "method":"test_method", "param":"dc0002ce00000001ce00000002", "sig_alg":1, "signature":""}

从log可以看到,获取到输入的字段分别为1、2,经过改写成3、4,然后再保存,最后读取出来反序化后仍然可以获取到预期的值,即字段分别为3、4:

2018-08-08 18:38:34 [INF] vm/wasm/exec/env_func.go:431 prints(): VM: func prints: data from input param:

2018-08-08 18:38:34 [INF] vm/wasm/exec/env_func.go:405 printi(): VM: from contract:example, method:test_method, func printi: 1

2018-08-08 18:38:34 [INF] vm/wasm/exec/env_func.go:405 printi(): VM: from contract:example, method:test_method, func printi: 2

......

2018-08-08 18:38:34 [INF] vm/wasm/exec/env_func.go:431 prints(): VM: func prints: data from db:

2018-08-08 18:38:34 [INF] vm/wasm/exec/env_func.go:405 printi(): VM: from contract:example, method:test_method, func printi: 3

2018-08-08 18:38:34 [INF] vm/wasm/exec/env_func.go:405 printi(): VM: from contract:example, method:test_method, func printi: 4

2.5. 调用其他合约

下面是一个计算合约,目前实现了add方法,即计算两个参数的和:

#include "contractcomm.hpp"
#include "string.hpp"

#define ERROR_PARAM (-1)
#define ERROR_UNPACK (-2)
#define ERROR_INVALID_METHOD (-3)

struct TestStruct {
    uint32_t valueA;
    uint32_t valueB;
};

static bool unpack_struct(MsgPackCtx *ctx, TestStruct *info)
{    
    uint32_t size = 0;

    if (!unpack_array(ctx, &size)) return false; 
    if (2 != size) return false;

    if (!unpack_u32(ctx, &info->valueA)) return false; 
    if (!unpack_u32(ctx, &info->valueB)) return false; 

    return true;
}

static bool pack_struct(MsgPackCtx *ctx, TestStruct *info)
{ 
    if (!pack_array16(ctx, 2)) return false;

    if (!pack_u32(ctx, info->valueA)) return false; 
    if (!pack_u32(ctx, info->valueB)) return false; 

    return true;
}

int start(char* method) 
{
    if (0 == strcmp("add", method))
    {
        char param[PARAM_MAX_LEN];
        uint32_t paramLen = 0;    

        /* 获取传入的参数 */
        paramLen = getParam(param, PARAM_MAX_LEN);

        if (0 == paramLen) 
        {
            myprints("paramLen is 0, error");
            return ERROR_PARAM;
        }

        /* 反序列化参数,得到原始的数据 */
        MsgPackCtx ctx;
        msgpack_init(&ctx, (char*)param, paramLen);
        TestStruct testStruct;
        bool suc = unpack_struct(&ctx, &testStruct);
        if (!suc)
        {
            myprints("unpack struct error");

            return ERROR_UNPACK;
        }

        printi(testStruct.valueA);
        printi(testStruct.valueB);

        /* 打印两个参数的和 */        
        myprints("add result is:");
        printi(testStruct.valueA + testStruct.valueB);
    }
    else
    {
        myprints("invalid method");

        return ERROR_INVALID_METHOD;
    }

    return 0;
}

然后我们再写一个合约,在这个合约里面调用上面的计算合约,即传给上面的计算合约两个参数,在计算合约里计算这两个参数的和:(假设上面的计算合约已经部署在calccontract账号上)

#include "contractcomm.hpp"
#define ERROR_UNPACK  (-1)
#define ERROR_CALLTRX (-2)

struct TestStruct {
    uint32_t valueA;
    uint32_t valueB;
};

static bool unpack_struct(MsgPackCtx *ctx, TestStruct *info)
{    
    uint32_t size = 0;

    if (!unpack_array(ctx, &size)) return false; 
    if (2 != size) return false;

    if (!unpack_u32(ctx, &info->valueA)) return false; 
    if (!unpack_u32(ctx, &info->valueB)) return false; 

    return true;
}

static bool pack_struct(MsgPackCtx *ctx, TestStruct *info)
{    
    if (!pack_array16(ctx, 2)) return false;

    if (!pack_u32(ctx, info->valueA)) return false; 
    if (!pack_u32(ctx, info->valueB)) return false; 

    return true;
}

int start(char* method) 
{
    char param[PARAM_MAX_LEN];
    TestStruct testStruct;

    testStruct.valueA = 3;
    testStruct.valueB = 4;    

    /* 序列化操作 */
    MsgPackCtx ctx;
    msgpack_init(&ctx, (char*)param, PARAM_MAX_LEN);
    bool suc = pack_struct(&ctx, &testStruct);
    if (!suc)
    {
        myprints("pack struct error");        
        return ERROR_UNPACK;
    }  

    /* 定义好要调用的合约名,以及方法名 */
    char *callContractName = "calccontract";
    char *callMethod = "add";

    /* 调用合约 */
    uint32_t callResult = callTrx(callContractName , strlen(callContractName), callMethod, strlen(callMethod), ctx.buf , ctx.pos);
    if (0 != callResult)
    {
        myprints("call trx error");        
        return ERROR_CALLTRX;
    }

    myprints("call trx succed");       

    return 0;
}

我们来调用上面的这个合约,将有如下log:

2018-08-09 12:00:34 [INF] vm/wasm/exec/env_func.go:431 prints(): VM: func prints: call trx succed

2018-08-09 12:00:34 [INF] vm/wasm/exec/env_func.go:405 printi(): VM: from contract:calccontract, method:add, func printi: 3

2018-08-09 12:00:34 [INF] vm/wasm/exec/env_func.go:405 printi(): VM: from contract:calccontract, method:add, func printi: 4

2018-08-09 12:00:34 [INF] vm/wasm/exec/env_func.go:431 prints(): VM: func prints: add result is:

2018-08-09 12:00:34 [INF] vm/wasm/exec/env_func.go:405 printi(): VM: from contract:calccontract, method:add, func printi: 7

2.6. 附1:供合用调用的接口函数

  • void prints(char *str, uint32_t len);

    功能:打印字符串

    参数说明:

    | 参数 | 类型 | 说明 | | ---- | -------- | ------------------ | | str | char* | 要打印的字符指针 | | len | uint32_t | 要打印的字符串长度 |

  • void printi(uint64_t param);

    | 参数 | 类型 | 说明 | | ----- | -------- | ------------ | | param | uint64_t | 要打印的整数 |

  • uint32_t setBinValue(char object, uint32_t objLen, char key, uint32_t keyLen, char *value, uint32_t valLen);

    功能:保存数据至链上

    参数说明:

    | 参数 | 类型 | 说明 | | ------ | -------- | ---------------------------- | | object | char | 保存的数据对应的表名称 | | objLen | uint32_t | 保存的数据对应的表名称的长度 | | key | char | 保存的数据的键值名称 | | keyLen | uint32_t | 保存的数据的键值名称的长度 | | value | char* | 要保存的数据 | | valLen | uint32_t | 要保存的数据的长度 |

    返回值:保存的数据长度

  • uint32_t getBinValue(char contract, uint32_t contractLen, char object, uint32_t objLen, char key, uint32_t keyLen, char valueBuf, uint32_t valueBufLen);

    功能:从链上获取数据

    参数说明:

    | 参数 | 类型 | 说明 | | ----------- | -------- | ---------------------------- | | contract | char | 获取的数据对应的合约名 | | contractLen | uint32_t | 获取的数据对应的合约名的长度 | | object | char | 获取的数据对应的表名称 | | objLen | uint32_t | 获取的数据对应的表名称的长度 | | key | char | 获取的数据的键值名称 | | keyLen | uint32_t | 获取的数据的键值名称的长度 | | valueBuf | char | 用于存储数据的缓冲区 | | valueBufLen | uint32_t | 用于存储数据的缓冲区的长度 |

    返回值:获取的数据的长度

  • uint32_t removeBinValue(char object, uint32_t objLen, char key, uint32_t keyLen);

    功能:从链上删除数据

    参数说明:

    | 参数 | 类型 | 说明 | | ------ | -------- | ---------------------------- | | object | char | 删除的数据对应的表名称 | | objLen | uint32_t | 删除的数据对应的表名称的长度 | | key | char | 删除的数据的键值名称 | | keyLen | uint32_t | 删除的数据的键值名称的长度 |

    返回值:删除的数据的长度

  • uint32_t getParam(char *param, uint32_t bufLen);

    功能:获取调用合约时参数

    参数说明:

    | 参数 | 类型 | 说明 | | ------ | -------- | ---------------- | | param | char* | 存储参数的缓冲区 | | bufLen | uint32_t | 存储参数的缓冲区 |

    返回值:获取的参数的长度

  • bool callTrx(char contract , uint32_t contractLen, char method , uint32_t methodLen, char *buf , uint32_t bufLen );

    功能:调用其他合约(异步方式)

    参数说明:

    | 参数 | 类型 | 说明 | | ----------- | -------- | -------------------------- | | contract | char | 要调用合约的名称 | | contractLen | uint32_t | 要调用合约的名称的长度 | | method | char | 要调用合约的方法名称 | | methodLen | uint32_t | 要调用合约的方法名称的长度 | | buf | char* | 调用合约传入的参数 | | bufLen | uint32_t | 调用合约传入的参数的长度 |

    返回值:true: 成功,false: 失败

  • uint32_t getCtxName(char *str , uint32_t len);

    功能:获取合约名称

    参数说明:

    | 参数 | 类型 | 说明 | | ---- | -------- | -------------------------- | | str | char* | 存储合约名称的缓冲区 | | len | uint32_t | 存储合约名称的缓冲区的长度 |

    返回值:所获取账户名称的长度

  • uint32_t getSender(char *str , uint32_t len);

    功能:获取调用当前合约的账户名称

    参数说明:

    | 参数 | 类型 | 说明 | | ---- | -------- | -------------------------- | | str | char* | 存储账户名称的缓冲区 | | len | uint32_t | 存储账户名称的缓冲区的长度 |

    返回值:所获取账户名称的长度

  • bool isAccountExist(char *name, uint32_t nameLen);

    功能:检查账户是否存在

    参数说明:

    | 参数 | 类型 | 说明 | | ------- | -------- | ------------------------ | | name | char* | 所要检查的账户 | | nameLen | uint32_t | 所要检查的账户名称的长度 |

    返回值:true: 存在,false: 不存在

2.7. 附2:MessagePack序列化

2.7.1 概述

为了便于调用合约时的参数传输,以及读取合约的持久化数据,我们选择了MessagePack这种轻量级的编解码方法,详细的规范参考:

https://msgpack.org/

https://github.com/msgpack/msgpack/blob/master/spec.md

另外针对合约数据的特点,我们进行了一些裁剪:

  1. 基本类型:支持uint8、uint16、uint32、uint64、array16、bin16、str16类型;

  2. 变长数据:比如str类型,MessagePack原规范根据字符串长度填写长度字节,有1、2、4字节长度的不同,改造后默认使用2字节(str16),bin类型和array类型也是,即只支持bin16、array16这种类型;

  3. 结构体:采用array形式封装结构体,编码时首先写array16头,再依次编码各字段。

例子(C):

struct user_login {
    char user_name[USER_NAME_MAX_LEN];
    uint32_t random_num;
};

user_login login;
strcpy(login.user_name, "testuser");
login.random_num = 99;

pack_array16(&ctx, 2);
pack_str16(&ctx, login->user_name, strlen(login->user_name));
pack_u32(&ctx, login->random_num);

编码结果:

0xdc, 0x00, 0x02, 0xda, 0x00, 0x08, 0x74, 0x65, 0x73, 0x74, 0x75, 0x73, 0x65, 0x72, 0xce, 0x00, 0x00, 0x00, 0x63

2.7.2 编码规范

Types

  • Integer represents an integer
  • Raw
    • String extending Raw type represents a UTF-8 string
    • Binary extending Raw type represents a byte array
  • Array represents a sequence of objects

Format

format name first byte (in binary) first byte (in hex)
bin16 11000101 0xc5
uint8 11001100 0xcc
uint16 11001101 0xcd
uint32 11001110 0xce
uint64 11001111 0xcf
str16 11011010 0xda
array16 11011100 0xdc

Notation in diagrams

one byte:
+--------+
|        |
+--------+

a variable number of bytes:
+========+
|        |
+========+

variable number of objects stored in MessagePack format:
+~~~~~~~~~~~~~~~~~+
|                 |
+~~~~~~~~~~~~~~~~~+

int format family

Int format family stores an integer in 2, 3, 5, or 9 bytes.

uint 32 stores a 32-bit big-endian unsigned integer
+--------+--------+--------+--------+--------+
|  0xce  |ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|
+--------+--------+--------+--------+--------+

uint 64 stores a 64-bit big-endian unsigned integer
+--------+--------+--------+--------+--------+--------+--------+--------+--------+
|  0xcf  |ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|
+--------+--------+--------+--------+--------+--------+--------+--------+--------+

str format family

Str format family stores a byte array in 3 bytes of extra bytes in addition to the size of the byte array.

str 16 stores a byte array whose length is upto (2^16)-1 bytes:
+--------+--------+--------+========+
|  0xda  |ZZZZZZZZ|ZZZZZZZZ|  data  |
+--------+--------+--------+========+

where
* ZZZZZZZZ_ZZZZZZZZ is a 16-bit big-endian unsigned integer which represents N
* N is the length of data

bin format family

Bin format family stores an byte array in 3 bytes of extra bytes in addition to the size of the byte array.

bin 16 stores a byte array whose length is upto (2^16)-1 bytes:
+--------+--------+--------+========+
|  0xc5  |YYYYYYYY|YYYYYYYY|  data  |
+--------+--------+--------+========+

where
* YYYYYYYY_YYYYYYYY is a 16-bit big-endian unsigned integer which represents N
* N is the length of data

array format family

Array format family stores a sequence of elements in 3 bytes of extra bytes in addition to the elements.

array 16 stores an array whose length is upto (2^16)-1 elements:
+--------+--------+--------+~~~~~~~~~~~~~~~~~+
|  0xdc  |YYYYYYYY|YYYYYYYY|    N objects    |
+--------+--------+--------+~~~~~~~~~~~~~~~~~+

where
* YYYYYYYY_YYYYYYYY is a 16-bit big-endian unsigned integer which represents N
    N is the size of a array

Serialization: type to format conversion

MessagePack serializers convert MessagePack types into formats as following:

source types output format
Integer int format family (positive fixint uint 8/16/32/64)
String str format family (str16)
Binary bin format family (bin16)
Array array format family (array16)

Deserialization: format to type conversion

MessagePack deserializers convert MessagePack formats into types as following:

source formats output type
positive fixint,uint 8/16/32/64 Integer
str16 String
bin16 Binary
array16 Array

results matching ""

    No results matching ""