4 构造和匹配二进制
二进制可以像如下例子一样高效地构造:
建议
my_list_to_binary(List) ->
my_list_to_binary(List, <<>>).
my_list_to_binary([H|T], Acc) ->
my_list_to_binary(T, <<Acc/binary,H>>);
my_list_to_binary([], Acc) ->
Acc.
二进制也可以像如下例子一样高效地匹配:
建议
my_binary_to_list(<<H,T/binary>>) ->
[H|my_binary_to_list(T)];
my_binary_to_list(<<>>) -> [].
4.1 二进制是如何实现的
二进制和位字符串在内部实现是一样的。在仿真器的源码中他们都被叫做二进制。
在内部有四种二进制的对象
● 两个是二进制数据的容器:
Refc二进制(引用计数二进制的缩写);
堆二进制;
● 两个仅仅是对二进制部分数据的引用:
子二进制;
匹配上下文;
引用计数二进制
引用计数二进制包含两个部分
● 一个对象保存在进程堆中,叫做 ProcBin;
● 二进制对象,保存在所有进程之外的堆中;
这个二进制对象可以被任意数量的 ProcBins 和任意数量的进程所引用。它持有一个应用计数器,用来追踪引用的数量。以便在最后一个引用消失之后将自身删除。
进程中所有的 ProcBin 对象都是链表的一部分。因此垃圾回收器可以追踪得到它们,而且当一个 ProcBin 消失的时候垃圾回收器可以减少二进制中的引用计数。
堆二进制
堆二进制是小的二进制数,最大为64位,并直接存储在进程堆中。当进程进行垃圾回收和将堆二进制当消息发送出去时,堆二进制会被复制。它们不需要垃圾回收器进行任何的特殊的处理。
子二进制
引用对象的子二进制和匹配上下文可以引用一部分引用计数二进制和堆二进制。
split_binary/2和二进制模式匹配时可以创建一个子二进制。它是其他二进制的部分引用(引用计数二进制或者堆二进制,但绝不可能是其他子二进制)。因此,使用匹配获取一个二进制是廉价的,因为实际上二进制数据不会被复制。
匹配上下文
匹配上下文与子二进制类似,但是针对二进制匹配进行了优化。例如:他包含一个直接指向二进制数据的指针。对于从二进制文件中匹配的每个字段,匹配上下文中的位置都会递增。(这句话看不懂。。。)
&Emsp; 编译器尽量避免生成创建子二进制的代码,只是快速地创建一个新的匹配上下文并保留之,然后丢弃子二进制。
在匹配上下文不会被分享出去的情况下,这是编译器唯一能进行的优化。如果它被共享,Erlang的函数属性(也称为引用透明性)将被破坏。(这句也看不太懂)
4.2 构造二进制
运行时系统对追加一个二进制和位字符串进行特殊的优化。
<<Binary/binary, ...>>
<<Binary/bitstring, ...>>
因为运行时系统处理此优化(而不是编译器),极少存在优化不生效的情况。
为了解释它是如何处理的,让我们一行行的测试如下例子的代码:
Bin0 = <<0>>, %% 1
Bin1 = <<Bin0/binary,1,2,3>>, %% 2
Bin2 = <<Bin1/binary,4,5,6>>, %% 3
Bin3 = <<Bin2/binary,7,8,9>>, %% 4
Bin4 = <<Bin1/binary,17>>, %% 5 !!!
{Bin4,Bin3} %% 6