Peter Saxton
Peter Saxton

Reputation: 4676

Matching multiple parts in an elixir binary for parsing HTTP/2.0 frames

I have discovered a quick way to match a frame from a binary. The length matches as an integer and part c (the payload) has as many octets as declared in the length field. (First three octets)

<<length::24, b::48, c::binary-size(length)>> <> rest = buffer

The problem is to get at my frame I need to recombine the parts.

frame = <<length::24, b::48, c::binary>>

Is there anyway to assign the frame variable in the original match. Something like the following. Though this precise version does not work

(frame = <<length::24, _::48, _::binary-size(length)>>) <> rest = buffer

Edit, or some syntax like the following would also make sense

<< frame = <<length::24, _::48, _::binary-size(length)>>, rest::binary>>

Upvotes: 2

Views: 243

Answers (2)

Azolo
Azolo

Reputation: 4383

I don't understand exactly what part of the header that b::48 signifies.

But anyway, performance wise it's important to consider a couple of things.

  1. You're not being performant by returning a frame binary. If you have parsed the frame and know it is complete then return it as a etf representation. Don't reparse it again.
  2. One line isn't necessarily more performant. It's all about combining and extracting binary parts in certain ways.
  3. Pattern Matching in function definitions is also very performant, do it as much as possible for different code paths.

I don't know how performant <> really is. I think it is just shorthand, but it's probably performant enough even if it different.

This bit of code should create and link binary parts and shouldn't create any new binaries. Therefore it is actually quite performant.

<<length::24, b::48, c::binary-size(length), rest::binary>> = buffer
frame = <<length::24, b::48, c::binary>>

If you want to see how I handled this kind of data then take a look at WebSockex.Frame.

Upvotes: 0

Aleksei Matiushkin
Aleksei Matiushkin

Reputation: 121000

AFAIK, it’s impossible exactly this way, but you might declare a handy helper to avoid repetitive typing:

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

And use it like:

{:ok, frame, rest} = matcher(buffer)

Upvotes: 1

Related Questions