匹配 Elixir 二进制文件中的多个部分以解析 HTTP/2.0 帧
Posted
技术标签:
【中文标题】匹配 Elixir 二进制文件中的多个部分以解析 HTTP/2.0 帧【英文标题】:Matching multiple parts in an elixir binary for parsing HTTP/2.0 frames 【发布时间】:2017-12-01 04:19:51 【问题描述】:我发现了一种从二进制文件中匹配帧的快速方法。 长度匹配为整数,并且 c 部分(有效负载)具有与长度字段中声明的一样多的八位字节。 (前三个八位字节)
<<length::24, b::48, c::binary-size(length)>> <> rest = buffer
问题是要到达我的框架,我需要重新组合部件。
frame = <<length::24, b::48, c::binary>>
无论如何在原始匹配中分配帧变量。类似于以下内容。 虽然这个精确的版本不起作用
(frame = <<length::24, _::48, _::binary-size(length)>>) <> rest = buffer
编辑,或者像下面这样的语法也有意义
<< frame = <<length::24, _::48, _::binary-size(length)>>, rest::binary>>
【问题讨论】:
您可能已经知道这一点,但为了避免重新创建二进制文件,您可以使用:binary.part(buffer, 0, 3 + 6 + length)
。 :binary.part/3
将从原始的 buffer
创建一个子二进制文件,而不是分配一个新的二进制文件。
我不知道这听起来很有帮助,仍然需要两步来获得我想要的二进制文件,但可能很快
【参考方案1】:
AFAIK,完全不可能这样,但你可以声明一个方便的助手来避免重复输入:
def matcher(buffer)
with <<length::24, b::48, c::binary-size(length), rest::binary>> <- buffer do
:ok, <<length::24, b::48, c::binary-size(length)>>, rest
else
other -> :error, other
end
end
并像这样使用它:
:ok, frame, rest = matcher(buffer)
【讨论】:
谢谢,不想听起来忘恩负义,但我已经走到了这一步:-)。我想知道是否可以使用一条线,因为它可能性能更高。不创建和连接临时二进制文件【参考方案2】:我不明白b::48
表示的标头的确切部分。
但无论如何,在性能方面考虑几件事很重要。
-
返回帧二进制文件并不能提高性能。如果您已经解析了框架并且知道它是完整的,那么将其作为
etf
表示形式返回。不要重新解析它。
一行不一定性能更好。这就是以某种方式组合和提取二进制部分。
函数定义中的模式匹配也非常高效,尽可能针对不同的代码路径进行匹配。
我不知道<>
的性能到底如何。我认为这只是简写,但即使它不同,它也可能足够高性能。
这段代码应该创建和链接二进制部分,而不应该创建任何新的二进制文件。因此,它实际上非常高效。
<<length::24, b::48, c::binary-size(length), rest::binary>> = buffer
frame = <<length::24, b::48, c::binary>>
如果您想了解我是如何处理此类数据的,请查看WebSockex.Frame
。
【讨论】:
您确定frame = <<length::24, b::48, c::binary>>
会共享原始二进制文件吗?我刚刚创建了一个包含单个帧的 1 MB 缓冲区,并创建了一个包含 100 个<<length::24, b::48, c::binary>>
的列表,我的内存使用量增加了 100MB。另一方面,如果我创建一个包含 100 个frame = :binary.part(buffer, 0, 9999999)
的列表,我的内存使用情况大致相同。
是的,但它是编译时优化。请参阅Erlang Binary Handling 页面上的匹配上下文。以上是关于匹配 Elixir 二进制文件中的多个部分以解析 HTTP/2.0 帧的主要内容,如果未能解决你的问题,请参考以下文章
[Elixir006]CSV(Comma-separated values)处理