查看源代码 ei
用于处理 Erlang 二进制项格式的例程。
描述
库 ei
包含用于编码和解码 Erlang 二进制项格式的宏和函数。
ei
允许您将原子、列表、数字和二进制数据转换为二进制格式,以及从二进制格式转换。这在编写端口程序和驱动程序时很有用。ei
使用给定的缓冲区,没有动态内存(除了 ei_decode_fun()
),并且通常速度很快。
ei
还处理 C 节点,即使用 Erlang 分布格式与 Erlang 节点(或其他 C 节点)进行 Erlang 分布通信的 C 程序。ei
库是线程安全的,并且使用线程,一个进程可以处理多个 C 节点。
解码和编码函数使用缓冲区和缓冲区中的索引,该索引指向编码和解码的位置。索引会更新为指向编码/解码的项之后的位置。不会检查该项是否适合缓冲区。如果编码超出缓冲区,程序可能会崩溃。
所有函数都接受两个参数
buf
是指向二进制数据所在的缓冲区或将要写入的缓冲区的指针。index
是指向缓冲区中索引的指针。此参数会随着解码/编码的项的大小而增加。
因此,当调用 ei
函数时,数据位于 buf[*index]
。
所有编码函数都假定 buf
和 index
参数指向足够大的缓冲区以容纳数据。请注意,二进制项格式使用可变长度编码,因此不同的值可能需要不同的空间大小。例如,较小的整数值可能比更大的整数值更紧凑。要在不编码的情况下获取编码项的大小,请传递 NULL
而不是缓冲区指针。参数 index
会递增,但不会编码任何内容。这是在 ei
中“预检”项编码的方法。
还有一些使用动态缓冲区的编码函数。通常,使用这些函数来编码数据会更方便。所有编码函数都有两个版本;以 ei_x_
开头的那些使用 ei_x_buff
类型的动态缓冲区。
如果成功,所有函数都返回 0
,否则返回 -1
(例如,如果项不是预期类型,或者要解码的数据是无效的 Erlang 项)。
某些解码函数需要预先分配的缓冲区。必须分配足够大的缓冲区,对于非复合类型,ei_get_type()
函数会返回所需的大小(请注意,对于字符串,需要额外的字节用于 NULL
终止符)。
数据类型
ei_term
typedef struct { char ei_type; int arity; int size; union { long i_val; double d_val; char atom_name[MAXATOMLEN_UTF8]; erlang_pid pid; erlang_port port; erlang_ref ref; } value; } ei_term;
由
ei_decode_ei_term()
写入的结构。ei_type
字段是项的类型,它等于ei_get_type()
将*type
设置为的值。ei_x_buff
- 动态调整大小的缓冲区。它是一个struct
,其中包含用户感兴趣的两个字段char *buff
- 指向动态分配缓冲区的指针。int index
- 指向要写入的下一个字节的偏移量,也等于当前写入的字节数。
ei_x_buff
通过调用ei_x_new()
或ei_x_new_with_version()
进行初始化。通过调用ei_x_free()
释放初始化的ei_x_buff
使用的内存。erlang_char_encoding
typedef enum { ERLANG_ASCII = 1, ERLANG_LATIN1 = 2, ERLANG_UTF8 = 4 } erlang_char_encoding;
用于原子的字符编码。
ERLANG_ASCII
表示 7 位 ASCII。Latin-1 和 UTF-8 是 7 位 ASCII 的不同扩展。所有 7 位 ASCII 字符都是有效的 Latin-1 和 UTF-8 字符。ASCII 和 Latin-1 都用一个字节表示每个字符。一个 UTF-8 字符可以由 1-4 个字节组成。请注意,这些常量是位标志,可以与按位或组合。erlang_fun
- 表示 Erlang 函数的不透明数据类型。erlang_pid
- 表示 Erlang 进程标识符的不透明数据类型。erlang_port
- 表示 Erlang 端口标识符的不透明数据类型。erlang_ref
- 表示 Erlang 引用的不透明数据类型。erlang_trace
- 表示 Erlang 顺序跟踪令牌的不透明数据类型。
ei_cmp_pids()
int ei_cmp_pids(erlang_pid *a, erlang_pid *b);
比较两个进程标识符。比较方式与 Erlang 相同。
如果 a
和 b
相等,则返回 0
。如果 a
的比较结果小于 b
,则返回小于 0
的值。如果 a
的比较结果大于 b
,则返回大于 0
的值。
自 OTP 23.0 起可用
ei_cmp_ports()
int ei_cmp_ports(erlang_port *a, erlang_port *b);
比较两个端口标识符。比较方式与 Erlang 相同。
如果 a
和 b
相等,则返回 0
。如果 a
的比较结果小于 b
,则返回小于 0
的值。如果 a
的比较结果大于 b
,则返回大于 0
的值。
自 OTP 23.0 起可用
ei_cmp_refs()
int ei_cmp_refs(erlang_ref *a, erlang_ref *b);
比较两个引用。比较方式与 Erlang 相同。
如果 a
和 b
相等,则返回 0
。如果 a
的比较结果小于 b
,则返回小于 0
的值。如果 a
的比较结果大于 b
,则返回大于 0
的值。
自 OTP 23.0 起可用
ei_decode_atom()
int ei_decode_atom(const char *buf, int *index, char *p);
从二进制格式解码原子。原子的以 NULL
结尾的名称放置在 p
处。缓冲区中最多可以放置 MAXATOMLEN
个字节。
ei_decode_atom_as()
int ei_decode_atom_as(const char *buf, int *index, char *p, int plen,
erlang_char_encoding want, erlang_char_encoding* was, erlang_char_encoding* result);
从二进制格式解码原子。原子的以 NULL
结尾的名称放置在 p
处长度为 plen
个字节的缓冲区中。
所需的字符串编码由 want
指定。二进制格式中使用的原始编码(Latin-1 或 UTF-8)可以从 *was
获取。结果字符串的编码(7 位 ASCII、Latin-1 或 UTF-8)可以从 *result
获取。was
和 result
都可以为 NULL
。如果 want
是按位或组合(如 ERLANG_LATIN1|ERLANG_UTF8
)或者如果 *result
结果是纯 7 位 ASCII(与 Latin-1 和 UTF-8 兼容),则 *result
可能与 want
不同。
如果原子对于缓冲区来说太长或无法用编码 want
表示,则此函数将失败。
此函数在 Erlang/OTP R16 中引入,作为支持 UTF-8 原子的第一步。
自 OTP R16B 起可用
ei_decode_bignum()
int ei_decode_bignum(const char *buf, int *index, mpz_t obj);
将二进制格式的整数解码为 GMP mpz_t
整数。要使用此函数,必须配置和编译 ei
库才能使用 GMP 库。
ei_decode_binary()
int ei_decode_binary(const char *buf, int *index, void *p, long *len);
从二进制格式解码二进制数据。参数 len
设置为二进制数据的实际大小。请注意,ei_decode_binary()
假定有足够的空间用于二进制数据。所需的大小可以通过 ei_get_type()
获取。
ei_decode_bitstring()
int ei_decode_bitstring(const char *buf, int *index, const char **pp,
unsigned int *bitoffsp, size_t *nbitsp);
从二进制格式解码位串。
pp
-NULL
或*pp
返回指向位串第一个字节的指针。只要buf
指向的缓冲区是可读的且未写入,返回的位串就是可读的。bitoffsp
-NULL
或*bitoffsp
返回*pp
指向的第一个字节中未使用的位数。*bitoffsp
的值介于 0 和 7 之间。第一个字节中未使用的位是最高有效位。nbitsp
-NULL
或*nbitsp
返回位串的长度(以位为单位)。
如果它是位串项,则返回 0
。
*pp
指向的字节数是位串的一部分,为 (*bitoffsp + *nbitsp + 7)/8
。如果 (*bitoffsp + *bitsp)%8 > 0
,则仅使用最后一个字节的 (*bitoffsp + *bitsp)%8
位。最后一个字节中未使用的位是最低有效位。
第一个字节和最后一个字节中未使用的位的值是未定义的,不能依赖它。
位数可以被 8 整除,这意味着可以通过 ei_decode_binary
解码的二进制数据也可以通过 ei_decode_bitstring
解码。
自 OTP 22.0 起可用
ei_decode_boolean()
int ei_decode_boolean(const char *buf, int *index, int *p);
从二进制格式解码布尔值。布尔值实际上是一个原子,true
解码为 1,false
解码为 0。
ei_decode_char()
int ei_decode_char(const char *buf, int *index, char *p);
从二进制格式解码 0-255 之间的字符 (8 位) 整数。由于历史原因,返回的整数类型为 char
。即使 C 编译器和系统可以将 char
定义为有符号的,您的 C 代码也应将返回的值视为 unsigned char
类型。
ei_decode_double()
int ei_decode_double(const char *buf, int *index, double *p);
从二进制格式解码双精度 (64 位) 浮点数。
ei_decode_ei_term()
int ei_decode_ei_term(const char* buf, int* index, ei_term* term);
解码任何项,或者至少尝试解码。如果 buf
中 *index
指向的项适合 term
联合,则会解码它,并且设置 term->value
中的相应字段,并且 *index
会递增项的大小。
如果成功解码,该函数返回 1
;如果发生错误,则返回 -1
;如果项看起来没问题,但与 term
结构不匹配,则返回 0
。如果返回 1
,则 index
会递增,并且 term
包含解码的项。
term
结构包含元组或列表的元数,以及二进制数据、字符串或原子的大小。如果它是以下任何一种类型,则包含一个项:整数、浮点数、原子、PID、端口或引用。
ei_decode_fun()
free_fun()
int ei_decode_fun(const char *buf, int *index, erlang_fun *p);
void free_fun(erlang_fun* f);
从二进制格式解码一个 fun。参数 p
必须为 NULL
或指向一个 erlang_fun
结构。这是唯一一个分配内存的解码函数。当不再需要 erlang_fun
时,应使用 free_fun
释放它。(这与 fun 的环境的任意大小有关。)
ei_decode_iodata()
int ei_decode_iodata(const char *buf, int *index, int *size, char *outbuf);
解码 iodata()
类型的项。 iodata/0
项将被展平并写入由 outbuf
参数指向的缓冲区。 iodata
的字节大小被写入由 size
参数指向的整数变量中。size
和 outbuf
都可以设置为 NULL
。无论 size
和 outbuf
参数的状态如何,由 index
参数指向的整数都会被更新,以引用 iodata/0
项之后的项。
请注意,如果将非 NULL
值作为 outbuf
传递,则由 outbuf
参数指向的缓冲区必须足够大。通常需要调用 ei_decode_iodata()
两次。第一次使用非 NULL
的 size
参数和 NULL
的 outbuf
参数,以确定所需的缓冲区大小,然后再次调用以执行实际解码。请注意,由 index
指向的整数也会通过确定大小的调用进行更新,因此在进行实际解码的第二次调用之前,需要重置它。
成功时返回 0
,失败时返回 -1
。失败可能是由于项的编码无效或由于项不是 iodata/0
类型。如果失败,由 index
参数指向的整数将更新为引用检测到失败的子项。
自 OTP 23.0 起可用
ei_decode_list_header()
int ei_decode_list_header(const char *buf, int *index, int *arity);
从二进制格式解码列表头。元素的数量在 arity
中返回。arity+1
个元素紧随其后(最后一个是列表的尾部,通常是空列表)。如果 arity
是 0
,则表示空列表。
请注意,如果列表完全由 0..255 范围内的整数组成,则会将其编码为字符串。此函数不解码此类字符串,请改用 ei_decode_string()
。
ei_decode_long()
int ei_decode_long(const char *buf, int *index, long *p);
从二进制格式解码一个长整数。如果代码是 64 位,则函数 ei_decode_long()
与 ei_decode_longlong()
相同。
ei_decode_longlong()
int ei_decode_longlong(const char *buf, int *index, long long *p);
从二进制格式解码一个 GCC long long
或 Visual C++ __int64
(64 位) 整数。
ei_decode_map_header()
int ei_decode_map_header(const char *buf, int *index, int *arity);
从二进制格式解码一个映射头。键值对的数量在 *arity
中返回。键和值按以下顺序排列:K1, V1, K2, V2, ..., Kn, Vn
。这总共有 arity*2
个项。如果 arity
为零,则表示空映射。正确编码的映射没有重复的键。
自 OTP 17.0 起可用
ei_decode_pid()
int ei_decode_pid(const char *buf, int *index, erlang_pid *p);
从二进制格式解码一个进程标识符 (pid)。
ei_decode_port()
int ei_decode_port(const char *buf, int *index, erlang_port *p);
从二进制格式解码一个端口标识符。
ei_decode_ref()
int ei_decode_ref(const char *buf, int *index, erlang_ref *p);
从二进制格式解码一个引用。
ei_decode_string()
int ei_decode_string(const char *buf, int *index, char *p);
从二进制格式解码一个字符串。Erlang 中的字符串是介于 0 和 255 之间的整数列表。请注意,由于字符串只是一个列表,有时列表会被 term_to_binary/1
编码为字符串,即使它并非本意。
字符串被复制到 p
,并且必须分配足够的空间。返回的字符串以 NULL
结尾,因此您必须为内存需求添加一个额外的字节。
ei_decode_trace()
int ei_decode_trace(const char *buf, int *index, erlang_trace *p);
从二进制格式解码一个 Erlang 跟踪标记。
ei_decode_tuple_header()
int ei_decode_tuple_header(const char *buf, int *index, int *arity);
解码一个元组头,元素的数量在 arity
中返回。元组元素按顺序在缓冲区中排列。
ei_decode_ulong()
int ei_decode_ulong(const char *buf, int *index, unsigned long *p);
从二进制格式解码一个无符号长整数。如果代码是 64 位,则函数 ei_decode_ulong()
与 ei_decode_ulonglong()
相同。
ei_decode_ulonglong()
int ei_decode_ulonglong(const char *buf, int *index, unsigned long long *p);
从二进制格式解码一个 GCC unsigned long long
或 Visual C++ unsigned __int64
(64 位) 整数。
ei_decode_version()
int ei_decode_version(const char *buf, int *index, int *version);
解码 Erlang 二进制项格式的版本魔数。它必须是二进制项中的第一个标记。
ei_encode_atom()
ei_encode_atom_len()
ei_x_encode_atom()
ei_x_encode_atom_len()
int ei_encode_atom(char *buf, int *index, const char *p);
int ei_encode_atom_len(char *buf, int *index, const char *p, int len);
int ei_x_encode_atom(ei_x_buff* x, const char *p);
int ei_x_encode_atom_len(ei_x_buff* x, const char *p, int len);
以二进制格式编码一个原子。参数 p
是以 Latin-1 编码的原子名称。只编码最多 MAXATOMLEN-1
个字节。名称必须以 NULL
结尾,ei_x_encode_atom_len()
函数除外。
ei_encode_atom_as()
自 OTP R16B 起可用
ei_encode_atom_len_as()
自 OTP R16B 起可用
ei_x_encode_atom_as()
自 OTP R16B 起可用
ei_x_encode_atom_len_as()
int ei_encode_atom_as(char *buf, int *index, const char *p,
erlang_char_encoding from_enc, erlang_char_encoding to_enc);
int ei_encode_atom_len_as(char *buf, int *index, const char *p, int len,
erlang_char_encoding from_enc, erlang_char_encoding to_enc);
int ei_x_encode_atom_as(ei_x_buff* x, const char *p,
erlang_char_encoding from_enc, erlang_char_encoding to_enc);
int ei_x_encode_atom_len_as(ei_x_buff* x, const char *p, int len,
erlang_char_encoding from_enc, erlang_char_encoding to_enc);
以二进制格式编码一个原子。参数 p
是具有字符编码 from_enc
(ASCII、Latin-1 或 UTF-8)的原子名称。名称必须以 NULL
结尾,或者必须使用带有 len
参数的函数变体。
如果 p
不是 from_enc
编码中的有效字符串,则编码失败。
参数 to_enc
被忽略。从 Erlang/OTP 20 开始,编码始终以 UTF-8 进行,这对于 Erlang/OTP R16 一样旧的节点都是可读的。
自 OTP R16B 起可用
ei_encode_bignum()
ei_x_encode_bignum()
int ei_encode_bignum(char *buf, int *index, mpz_t obj);
int ei_x_encode_bignum(ei_x_buff *x, mpz_t obj);
将 GMP mpz_t
整数编码为二进制格式。要使用此函数,必须配置和编译 ei
库以使用 GMP 库。
ei_encode_binary()
ei_x_encode_binary()
int ei_encode_binary(char *buf, int *index, const void *p, long len);
int ei_x_encode_binary(ei_x_buff* x, const void *p, long len);
以二进制格式编码二进制数据。数据位于 p
,长度为 len
字节。
ei_encode_bitstring()
自 OTP 22.0 起可用
ei_x_encode_bitstring()
int ei_encode_bitstring(char *buf, int *index, const char *p, size_t bitoffs, size_t nbits);
int ei_x_encode_bitstring(ei_x_buff* x, const char *p, size_t bitoffs, size_t nbits);
以二进制格式编码一个位串。
数据位于 p
。位串的长度为 nbits
位。 p
处数据的前 bitoffs
位未使用。位串的第一字节是 p[bitoffs/8]
。第一个字节 p[bitoffs/8]
的 bitoffs%8
最高有效位未使用。
位串的一部分的字节数为 (bitoffs + nbits + 7)/8
。如果 (bitoffs + nbits)%8 > 0
,则仅使用最后一个字节的 (bitoffs + nbits)%8
位。最后一个字节中未使用的位是最低有效位。
未使用的位的值将被忽略,无需清除。
自 OTP 22.0 起可用
ei_encode_boolean()
ei_x_encode_boolean()
int ei_encode_boolean(char *buf, int *index, int p);
int ei_x_encode_boolean(ei_x_buff* x, int p);
如果 p
不为零,则将布尔值编码为原子 true
,如果 p
为零,则编码为 false
。
ei_encode_char()
ei_x_encode_char()
int ei_encode_char(char *buf, int *index, char p);
int ei_x_encode_char(ei_x_buff* x, char p);
以二进制格式将字符(8 位)编码为 0-255 之间的整数。出于历史原因,整数参数的类型为 char
。即使 C 编译器和系统可能将 char
定义为有符号的,您的 C 代码也应将指定的参数视为 unsigned char
类型。
ei_encode_double()
ei_x_encode_double()
int ei_encode_double(char *buf, int *index, double p);
int ei_x_encode_double(ei_x_buff* x, double p);
以二进制格式编码双精度(64 位)浮点数。
如果浮点数不是有限数,则返回 -1
。
ei_encode_empty_list()
ei_x_encode_empty_list()
int ei_encode_empty_list(char* buf, int* index);
int ei_x_encode_empty_list(ei_x_buff* x);
编码一个空列表。它通常用在列表的末尾。
ei_encode_fun()
ei_x_encode_fun()
int ei_encode_fun(char *buf, int *index, const erlang_fun *p);
int ei_x_encode_fun(ei_x_buff* x, const erlang_fun* fun);
以二进制格式编码一个 fun。参数 p
指向一个 erlang_fun
结构。 erlang_fun
不会自动释放,如果编码后不再需要该 fun,则需要调用 free_fun
。
ei_encode_list_header()
ei_x_encode_list_header()
int ei_encode_list_header(char *buf, int *index, int arity);
int ei_x_encode_list_header(ei_x_buff* x, int arity);
编码一个列表头,并指定元数。接下来的 arity+1
项是列表的元素(实际上是其 arity
个 cons 单元)和尾部。列表和元组是递归编码的,因此列表可以包含另一个列表或元组。
例如,要编码列表 [c, d, [e | f]]
ei_encode_list_header(buf, &i, 3);
ei_encode_atom(buf, &i, "c");
ei_encode_atom(buf, &i, "d");
ei_encode_list_header(buf, &i, 1);
ei_encode_atom(buf, &i, "e");
ei_encode_atom(buf, &i, "f");
ei_encode_empty_list(buf, &i);
注意
可能看起来没有办法在事先不知道元素数量的情况下创建列表。但实际上是有一种方法的。请注意,列表
[a, b, c]
可以写成[a | [b | [c]]]
。使用这种方法,列表可以写成 conses。
在事先不知道元数的情况下编码列表
while (something()) {
ei_x_encode_list_header(&x, 1);
ei_x_encode_ulong(&x, i); /* just an example */
}
ei_x_encode_empty_list(&x);
ei_encode_long()
ei_x_encode_long()
int ei_encode_long(char *buf, int *index, long p);
int ei_x_encode_long(ei_x_buff* x, long p);
以二进制格式编码一个长整数。如果代码是 64 位的,则函数 ei_encode_long()
与 ei_encode_longlong()
相同。
ei_encode_longlong()
ei_x_encode_longlong()
int ei_encode_longlong(char *buf, int *index, long long p);
int ei_x_encode_longlong(ei_x_buff* x, long long p);
以二进制格式编码一个 GCC long long
或 Visual C++ __int64
(64 位) 整数。
ei_encode_map_header()
自 OTP 17.0 起可用
ei_x_encode_map_header()
int ei_encode_map_header(char *buf, int *index, int arity);
int ei_x_encode_map_header(ei_x_buff* x, int arity);
编码一个映射头,并指定元数。接下来编码的 arity*2
项将是映射的键和值,并按以下顺序编码: K1, V1, K2, V2, ..., Kn, Vn
。
例如,要编码映射 #{a => "Apple", b => "Banana"}
ei_x_encode_map_header(&x, 2);
ei_x_encode_atom(&x, "a");
ei_x_encode_string(&x, "Apple");
ei_x_encode_atom(&x, "b");
ei_x_encode_string(&x, "Banana");
正确编码的映射不能有重复的键。
自 OTP 17.0 起可用
ei_encode_pid()
ei_x_encode_pid()
int ei_encode_pid(char *buf, int *index, const erlang_pid *p);
int ei_x_encode_pid(ei_x_buff* x, const erlang_pid *p);
以二进制格式编码 Erlang 进程标识符 (pid)。参数 p
指向一个 erlang_pid
结构,该结构应该先前通过 ei_decode_pid()
、 ei_self()
获取,或者通过 ei_make_pid()
创建。
ei_encode_port()
ei_x_encode_port()
int ei_encode_port(char *buf, int *index, const erlang_port *p);
int ei_x_encode_port(ei_x_buff* x, const erlang_port *p);
以二进制格式编码 Erlang 端口。参数 p
指向一个 erlang_port
结构,该结构应该先前通过 ei_decode_port()
获取。
ei_encode_ref()
ei_x_encode_ref()
int ei_encode_ref(char *buf, int *index, const erlang_ref *p);
int ei_x_encode_ref(ei_x_buff* x, const erlang_ref *p);
以二进制格式编码 Erlang 引用。参数 p
指向一个 erlang_ref
结构,该结构应该先前通过 ei_decode_ref()
获取,或者通过 ei_make_ref()
创建。
ei_encode_string()
ei_encode_string_len()
ei_x_encode_string()
ei_x_encode_string_len()
int ei_encode_string(char *buf, int *index, const char *p);
int ei_encode_string_len(char *buf, int *index, const char *p, int len);
int ei_x_encode_string(ei_x_buff* x, const char *p);
int ei_x_encode_string_len(ei_x_buff* x, const char* s, int len);
以二进制格式编码一个字符串。(Erlang 中的字符串是一个列表,但在二进制格式中编码为字符数组。)该字符串必须以 NULL
结尾,除了 ei_x_encode_string_len()
函数之外。
ei_encode_trace()
ei_x_encode_trace()
int ei_encode_trace(char *buf, int *index, const erlang_trace *p);
int ei_x_encode_trace(ei_x_buff* x, const erlang_trace *p);
以二进制格式编码 Erlang 跟踪令牌。参数 p
指向一个 erlang_trace
结构,该结构应该先前通过 ei_decode_trace()
获取。
ei_encode_tuple_header()
ei_x_encode_tuple_header()
int ei_encode_tuple_header(char *buf, int *index, int arity);
int ei_x_encode_tuple_header(ei_x_buff* x, int arity);
编码一个元组头,并指定元数。接下来编码的 arity
项将是元组的元素。元组和列表是递归编码的,因此元组可以包含另一个元组或列表。
例如,要编码元组 {a, {b, {}}}
ei_encode_tuple_header(buf, &i, 2);
ei_encode_atom(buf, &i, "a");
ei_encode_tuple_header(buf, &i, 2);
ei_encode_atom(buf, &i, "b");
ei_encode_tuple_header(buf, &i, 0);
ei_encode_ulong()
ei_x_encode_ulong()
int ei_encode_ulong(char *buf, int *index, unsigned long p);
int ei_x_encode_ulong(ei_x_buff* x, unsigned long p);
以二进制格式编码一个无符号长整数。如果代码是 64 位的,则函数 ei_encode_ulong()
与 ei_encode_ulonglong()
相同。
ei_encode_ulonglong()
ei_x_encode_ulonglong()
int ei_encode_ulonglong(char *buf, int *index, unsigned long long p);
int ei_x_encode_ulonglong(ei_x_buff* x, unsigned long long p);
以二进制格式编码一个 GCC unsigned long long
或 Visual C++ unsigned __int64
(64 位) 整数。
ei_encode_version()
ei_x_encode_version()
int ei_encode_version(char *buf, int *index);
int ei_x_encode_version(ei_x_buff* x);
编码二进制格式的版本魔数。必须是二进制项中的第一个标记。
ei_get_type()
int ei_get_type(const char *buf, const int *index, int *type, int *size);
返回编码项的类型,存储在 *type
中,大小存储在 *size
中。对于字符串和原子,大小是不包括终止 NULL
的字符数。对于二进制文件和位串, *size
是字节数。对于列表、元组和映射, *size
是对象的元数。对于大整数, *size
是大整数绝对值的字节数。对于其他类型, *size
是 0。在所有情况下, index
保持不变。
目前, *type
是以下之一
ERL_ATOM_EXT - 使用
ei_decode_atom()
、ei_decode_atom_as()
或ei_decode_boolean()
解码。ERL_BINARY_EXT - 使用
ei_decode_binary()
、ei_decode_bitstring()
或ei_decode_iodata()
解码。ERL_BIT_BINARY_EXT - 使用
ei_decode_bitstring()
解码。ERL_FLOAT_EXT - 使用
ei_decode_double()
解码。ERL_NEW_FUN_EXT, ERL_FUN_EXT, ERL_EXPORT_EXT - 使用
ei_decode_fun()
解码。ERL_SMALL_INTEGER_EXT, ERL_INTEGER_EXT, ERL_SMALL_BIG_EXT, ERL_LARGE_BIG_EXT - 使用
ei_decode_char()
、ei_decode_long()
、ei_decode_longlong()
、ei_decode_ulong()
、ei_decode_ulonglong()
或ei_decode_bignum()
解码。ERL_LIST_EXT, ERL_NIL_EXT - 使用
ei_decode_list_header()
或ei_decode_iodata()
解码。ERL_STRING_EXT - 使用
ei_decode_string()
或ei_decode_iodata()
解码。ERL_MAP_EXT - 使用
ei_decode_map_header()
解码。ERL_PID_EXT - 使用
ei_decode_pid()
解码。ERL_PORT_EXT - 使用
ei_decode_port()
解码。ERL_NEW_REFERENCE_EXT - 使用
ei_decode_ref()
解码。ERL_SMALL_TUPLE_EXT, ERL_LARGE_TUPLE_EXT
使用ei_decode_tuple_header()
解码。
如果不关心数据,也可以通过使用 ei_skip_term()
跳过一个项,而不是解码它。
ei_init()
int ei_init(void);
初始化 ei
库。在调用 ei
库中的任何其他功能之前,应该调用此函数一次(且仅一次)。
成功时返回零。失败时返回 posix 错误代码。
自 OTP 21.3 起可用
ei_print_term()
ei_s_print_term()
int ei_print_term(FILE* fp, const char* buf, int* index);
int ei_s_print_term(char** s, const char* buf, int* index);
以纯文本格式将项打印到由 fp
指定的文件,或由 s
指向的缓冲区。它尝试模仿 Erlang shell 中的项打印。
在 ei_s_print_term()
中,参数 s
指向一个动态(malloc)分配的 BUFSIZ
字节的字符串或一个 NULL
指针。如果结果超过 BUFSIZ
个字符,此函数可以重新分配字符串(并且可以更新 *s
)。返回的字符串以 NULL
结尾。
返回值是写入文件或字符串的字符数,如果 buf[index]
不包含有效的项,则返回 -1
。遗憾的是,不会检查 fp
上的 I/O 错误。
参数 index
会被更新,也就是说,这个函数可以被视为一个解码函数,将一个项解码成人类可读的格式。
ei_set_compat_rel()
void ei_set_compat_rel(unsigned release_number);
通常,ei
库保证与比 ei
库本身旧或新 2 个主要版本的其他 Erlang/OTP 组件兼容。
有时,为了使新功能(甚至错误修复)成为可能,必须对上述规则进行例外处理。调用 ei_set_compat_rel(release_number)
将 ei
库设置为 OTP 版本 release_number
的兼容模式。
目前,release_number
的唯一有用值是 21
。只有当从连接的节点接收到 *位字符串* 或 *导出函数* 时,这才会起作用并产生影响。在 OTP 22 之前,ei
不支持位字符串和导出函数。当从模拟器发送到 ei
时,它们改为使用未记录的回退元组格式进行编码。
位字符串
- 项<<42, 1:1>>
被编码为{<<42, 128>>, 1}
。元组的第一个元素是一个二进制数据,第二个元素表示最后字节中有多少位是位字符串的一部分。在本例中,只有最后一个字节 (128) 的最高有效位是位字符串的一部分。导出函数
- 项fun lists:map/2
被编码为{lists,map}
。一个包含模块、函数和缺少元数的元组。
如果 *没有* 调用 ei_set_compat_rel(21)
,则连接的模拟器将发送正确编码的位字符串和导出函数。必须使用函数 ei_decode_bitstring
和 ei_decode_fun
来解码这些项。调用 ei_set_compat_rel(21)
应该只作为一种解决方法,以保持旧的实现继续工作,该实现期望接收位字符串和/或导出函数的未记录的元组格式。
注意
如果调用此函数,则只能调用一次,并且必须在调用
ei
库中的任何其他函数之前调用。
ei_skip_term()
int ei_skip_term(const char* buf, int* index);
跳过指定缓冲区中的一个项;递归地跳过列表和元组的元素,以便跳过一个完整的项。这是获取 Erlang 项大小的一种方法。
buf
是缓冲区。
index
会被更新,以指向缓冲区中该项之后的位置。
注意
当您想保存任意项时,这很有用:跳过它们并将二进制项数据复制到某个缓冲区。
成功时返回 0
,否则返回 -1
。
ei_x_append()
ei_x_append_buf()
int ei_x_append(ei_x_buff* x, const ei_x_buff* x2);
int ei_x_append_buf(ei_x_buff* x, const char* buf, int len);
将数据追加到缓冲区 x
的末尾。
ei_x_format()
ei_x_format_wo_ver()
int ei_x_format(ei_x_buff* x, const char* fmt, ...);
int ei_x_format_wo_ver(ei_x_buff* x, const char *fmt, ... );
将以字符串形式给出的项格式化到缓冲区中。类似于 Erlang 项的 sprintf。fmt
包含格式字符串,带有 ~d
之类的参数,用于插入变量中的项。支持以下格式(带有给定的 C 类型)
~a An atom, char*
~c A character, char
~s A string, char*
~i An integer, int
~l A long integer, long int
~u A unsigned long integer, unsigned long int
~f A float, float
~d A double float, double float
~p An Erlang pid, erlang_pid*
例如,要编码一个包含一些内容的元组
ei_x_format("{~a,~i,~d}", "numbers", 12, 3.14159)
encodes the tuple {numbers,12,3.14159}
ei_x_format_wo_ver()
将格式化到缓冲区中,而不包含初始版本字节。
更改
自 OTP 26.2 起,可以使用类似于
"#{k1 => v1, k2 => v2}"
的语法对映射进行编码。
ei_x_free()
int ei_x_free(ei_x_buff* x);
释放 x
引用的缓冲区的动态分配内容。释放后,buff
字段设置为 NULL
。
ei_x_new()
ei_x_new_with_version()
int ei_x_new(ei_x_buff* x);
int ei_x_new_with_version(ei_x_buff* x);
初始化 x
引用的动态可实现缓冲区。填充参数 x
指向的结构的字段,并分配一个默认缓冲区。ei_x_new_with_version()
还放置一个初始版本字节,该字节在二进制格式中使用(因此不需要 ei_x_encode_version()
。)
调试信息
关于当模拟器似乎没有接收到您发送的项时,应检查的一些提示
- 请注意版本标头,在适当的时候使用
ei_x_new_with_version()
。 - 在 Erlang 节点上启用分布式跟踪。
- 检查
ei_decode_-calls
的结果代码。