1、引言
erlang:term_to_binary/1,2函数返回值是Erlang扩展term格式(Erlang external term format)的binary,即ext_binary(),
这个函数能把Erlang数据封装成二进制流,是一种存储和传输Erlang数据的有效途径,
甚至可以用这种封包/解包方式用作Socket的通信协议(某页游项目就是这么干的)。
读懂Erlang扩展term格式的二进制数据,你就可以用任意语言解包Erlang数据。
直接分析二进制流,语言之间沟通无限。
2、初识Erlang的ext_binary
2.1、从简单实例说起...
Eshell V5.10.1 (abort with ^G)
1> term_to_binary({}).
<<131,104,0>>
2> term_to_binary({1}).
<<131,104,1,97,1>>
3> term_to_binary({1,2}).
<<131,104,2,97,1,97,2>>
4> term_to_binary([]).
<<131,106>>
5> term_to_binary([1]).
<<131,107,0,1,1>>
6> term_to_binary([1,2]).
<<131,107,0,2,1,2>>
2.2、从简单实例思考...
(1) 为什么第1字节都是131? (2) 为什么tuple的第2字节都是104? (3) 为什么空list第2字节是106? (4) 为什么非空list第2字节都是107? (5) ...
2.3、在C源码中找答案...
$ERL_TOP/erts/emulator/beam/external.h
$ERL_TOP/erts/emulator/beam/external.c
从函数erts_term_to_binary(Process* p, Eterm Term, int level, Uint flags)中可以看到,
首先是在第一字节压入一个版本号:
#define VERSION_MAGIC 131 /* 130 in erlang 4.2 */
/* Increment this when changing the external format. */
/* ON the other hand, don't change the external format */
/* since that breaks other people's code! */
/*
* Get a pointer to the binary bytes, for a heap or refc binary
* (NOT sub binary).
*/
#define binary_bytes(Bin) \
(*binary_val(Bin) == HEADER_PROC_BIN ? \
((ProcBin *) binary_val(Bin))->bytes : \
(ASSERT_EXPR(thing_subtag(*binary_val(Bin)) == HEAP_BINARY_SUBTAG), \
(byte *)(&(((ErlHeapBin *) binary_val(Bin))->data))))
bin = new_binary(p, (byte *)NULL, size);
bytes = binary_bytes(bin);
bytes[0] = VERSION_MAGIC;
然后进入函数enc_term,来看一下enc_term中的重要片断:
switch(tag_val_def(obj)) {
case NIL_DEF:
// 如果是空列表,则压入NIL_EXT标识
*ep++ = NIL_EXT;
break;
// ......
case LIST_DEF:
{
int is_str;
i = is_external_string(obj, &is_str);
if (is_str) {
// 如果是字符list,则压入STRING_EXT标识
*ep++ = STRING_EXT;
put_int16(i, ep);
ep += 2;
while (is_list(obj)) {
Eterm* cons = list_val(obj);
*ep++ = unsigned_val(CAR(cons));
obj = CDR(cons);
}
} else {
// 如果是list,则压入LIST_EXT标识
*ep++ = LIST_EXT;
put_int32(i, ep);
ep += 4;
goto encode_one_cons;
}
}
break;
case TUPLE_DEF:
ptr = tuple_val(obj);
i = arityval(*ptr);
ptr++;
if (i <= 0xff) {
// 如果是小tuple,则压入SMALL_TUPLE_EXT标识
*ep++ = SMALL_TUPLE_EXT;
put_int8(i, ep);
ep += 1;
} else {
*ep++ = LARGE_TUPLE_EXT;
put_int32(i, ep);
ep += 4;
}
if (i > 0) {
WSTACK_PUSH(s, ENC_LAST_ARRAY_ELEMENT+i-1);
WSTACK_PUSH(s, (UWord) ptr);
}
break;
// ......
}
从这里可以了解到,ext_binary数据封包其实就是不同的数据类型前面给予不同的标识,
这些标识究竟是怎么定义的?请结合Erlang内部数据结构,往下看...
2.4、从简单实例答疑...
/* Same order as the ordering of terms in erlang */
/* Since there are 255 different External tag values to choose from
There is no reason to not be extravagant.
Hence, the different tags for large/small tuple e.t.c
*/
#ifdef ERTS_WANT_EXTERNAL_TAGS
#ifndef ERTS_EXTERNAL_TAGS
#define ERTS_EXTERNAL_TAGS
#define SMALL_INTEGER_EXT 'a'
#define INTEGER_EXT 'b'
#define FLOAT_EXT 'c'
#define ATOM_EXT 'd'
#define SMALL_ATOM_EXT 's'
#define REFERENCE_EXT 'e'
#define NEW_REFERENCE_EXT 'r'
#define PORT_EXT 'f'
#define NEW_FLOAT_EXT 'F'
#define PID_EXT 'g'
#define SMALL_TUPLE_EXT 'h'
#define LARGE_TUPLE_EXT 'i'
#define NIL_EXT 'j'
#define STRING_EXT 'k'
#define LIST_EXT 'l'
#define BINARY_EXT 'm'
#define BIT_BINARY_EXT 'M'
#define SMALL_BIG_EXT 'n'
#define LARGE_BIG_EXT 'o'
#define NEW_FUN_EXT 'p'
#define EXPORT_EXT 'q'
#define FUN_EXT 'u'
#define ATOM_UTF8_EXT 'v'
#define SMALL_ATOM_UTF8_EXT 'w'
#define DIST_HEADER 'D'
#define ATOM_CACHE_REF 'R'
#define ATOM_INTERNAL_REF2 'I'
#define ATOM_INTERNAL_REF3 'K'
#define BINARY_INTERNAL_REF 'J'
#define BIT_BINARY_INTERNAL_REF 'L'
#define COMPRESSED 'P'
我们先把问题中的几个ASCII码转换成对应字符:
Eshell V5.10.1 (abort with ^G)
1> [104,106,107].
"hjk"
根据这些字符,可以从在上面找到相应的宏,现在可解答上面的问题了:
(1) 为什么第1字节都是131? 答:它是一个版本号。 (2) 为什么tuple的第2字节都是104? 答:它是一个SMALL_TUPLE_EXT标识。 (3) 为什么空list第2字节是106? 答:它是一个NIL_EXT标识,表示一个空list。 (4) 为什么非空list第2字节都是107? 答:它是一个STRING_EXT标识,表示一个字符串,在Erlang中字符串其实就是list,但list不一定是字符串。 (5) ...
3、小结
研究Erlang的ext_binary,可以从term_to_binary和binary_to_term这两个函数的内部实现入手,一层层深入,本文只是抛砖引玉,有兴趣的同学可以继续深入研究,有成果别忘了通知一声,分享分享,共同进步!
关于External Term Format,更多内容可参阅官方文档:http://www.erlang.org/doc/apps/erts/erl_ext_dist.html
PS:感谢@蓝色心情 告知关于External Term Format的官方文档链接。