Ridtt

Ridtt

BinStruct - library for writing declarative code to get rich generated set of binary parsing/encoding features

Hex: bin_struct | Hex
Git: GitHub - 4ait/bin_struct
Docs: bin_struct v0.2.34 — Documentation

BinStruct is a library which main function is to convert readable as possible declarations to robust and performant implementations. It will generate set of functions to make parse → decode → create_new → send process trivial.

What BinStruct is not

BinStruct is not an protocol itself, there is no goal to replace asn1, protobuf, erlang binary term or any other protocols. if you can solve your problem using existing protocol - stick with it.

BinStruct is not replacement for binary pattern matching. If your job can be done via pattern match only it will be always better to use it directly. There is layer of complexity this lib adds to make it achive it’s main goal - write declarations, generate implementations automatically. When complexity grows only sane way to keep with it has general declarative structure of each part you working this.

BinStruct is by no means a framework and does not force you to follow any specific structure of how its parts will be used together. Each BinStruct you create is completely self-contained and can be used as you see fit. Whether you want to validate CRC, add encryption, or implement something else inside or outside—it’s entirely up to you, and the library imposes no restrictions on these choices.

What BinStruct is primarily

BinStruct is s tool. Tool to support developer from very beggining with reach set of generated features, allowing to exlore data in every step, to very end running your app in production.

I believe BinStruct is an essential tool for developers. Simply transferring declarations from your protocol documentation into BinStruct special syntax is enough to start parsing your data, decoding it, and exploring its structure. This lets you build an understanding of how to proceed next. It is especially helpful when working with a protocol that is new to you. If you’re unsure where to start or what to focus on, just transfer what you see in the documentation into BinStruct declarations and experiment. At some point, things will start falling into place, and you might even find that the application almost writes itself before you realize it. Even the smallest fragments you implement can already be put to use. You can parse and decode binary data to gain a better understanding of what you’re dealing with without needing to fully implement every detail or dynamic callback. You’ll gradually build out your protocol implementation step by step, and over time, these pieces will naturally connect as your codebase grows. You don’t need all the advanced features like virtual fields, auto-generated fields (builders), or type conversions beyond the basic managed (human-readable) one right away. You can always add them later if you think they’ll make the process easier.

Basic syntax overview

 
 defmodule PngChunk do
 
   use BinStruct
 
   #all dynamic behaviour is callback
   #if we are not specifying type_conversion this is always 'managed' also known as 'human readable'
   register_callback &data_length/1, length: :field
 
   #with fields you build shape of your binary data
   field :length, :uint32_be
 
   #use expanded constructs whenever possible, this is both easier to read and will be validated at parse time
   #its always better to expand arrays/flags/enums even if you don't use them for now, it will help moving forward
   #as you will have more complete picture
   #and also it will give you opportunity to be dispatched as dynamic variant later (read it as if we received something and it has type distinct from listed below it's not this struct, we can catch it via upper variant_of later)
   field :type, {
     :enum,
     %{
       type: :binary,
       values: [
         "IHDR",
         "PLTE",
         "IDAT",
         "IEND",
         "cHRM",
         "gAMA",
         "iCCP",
         "sBIT",
         "sRGB",
         "bKGD",
         "hIST",
         "tRNS",
         "pHYs",
         "sPLT",
         "tIME",
         "tEXt",
         "zTXt",
         "iTXt"
       ]
     }
   }, length: 4
 
   #consuming dynamic behaviour into length_by
   field :data, :binary, length_by: &data_length/1
 
   field :crc, :uint32_be
 
   #dynamic behaviour implementation
   #we returning always 'managed' type conversion, in this case length field will be automatically converted to elixir number
   #and we return this number as it
   defp data_length(length), do: length
 
 end


Performance notes

The library compiles into Elixir binary pattern match and uses optimizations like composing every part with known size into single pattern, always inlining for encoders and static values, caching every requested value.

If in registered_callback field A requested from both B and C , A will be converted to requested type conversion before B (late as possible) and later passed same value to C.
All functions, except for main public function like parse/2, are declared in the same module and marked as private (defp), giving maximum optimization opportunities for the Erlang compiler (erlc).

You can expect performance equal to manually written pattern matches, with some differences: modular structure, validation after each step, and creating structs as the result. It is not correct to compare simple manual parsing patterns directly to what this library does.


I have created small intro post few days ago about it: What is the Elixir way of decoding/parsing binary data? - #20 by Ridtt

I also included example implementation of png parser using BinStruct as alternative to suggested in article in this thread way using raw pattern matching.

For anyone interested in I suggest to start exploring with png example bin_struct/examples/png.exs at master · 4ait/bin_struct · GitHub

Then docs for main macros BinStruct — bin_struct v0.2.34

And then docs for types binary — bin_struct v0.2.34

More complex examples:

When things are hidden in integer: bin_struct/examples/extraction_from_integer.exs at master · 4ait/bin_struct · GitHub

When things are hidden in buffer: bin_struct/examples/extraction_from_buffer.exs at master · 4ait/bin_struct · GitHub

Implementing transport packet: bin_struct/examples/packet_via_higher_order_macro.exs at master · 4ait/bin_struct · GitHub

Dynamically working with recursive data structures: bin_struct/examples/recursive_sequence.exs at master · 4ait/bin_struct · GitHub


Future perfomance improvements:

Problem: you don’t always need all values to be decoded. And it’s always will be not optimal solution no matter there will be decode single field function or not. We can solve it with compile time use cases.

Compile time decode_only use case: bin_struct/examples/compiled_decode_use_case.exs at master · 4ait/bin_struct · GitHub

Most Liked

VictorGaiva

VictorGaiva

Very interesting.

It was a bit hard to understand what the lib actually does from this post. There could be some examples use cases.

I was able to understand better by reading the docs.

Great job with the project. Very specific use case, but greatly done.

Asd

Asd

Hi, good library, I’ve read the example and it looks promising! I’ve briefly peeked into the code and it looks interesting, and it has some special formatting, I love that. I also found that some modules are empty and exist only for sake of documentation. I’d suggest to just write separate .md doc files and expose them in hexdocs (absinthe documentation is a good example of how to do this).

I’ll definitely give it a deeper look in some near future!

Ridtt

Ridtt

Hello there, thank you. I just have added more complex examples as I promised in intro post.

When things are hidden in integer: bin_struct/examples/extraction_from_integer.exs at master · 4ait/bin_struct · GitHub

When things are hidden in buffer: bin_struct/examples/extraction_from_buffer.exs at master · 4ait/bin_struct · GitHub

Implementing transport packet: bin_struct/examples/packet_via_higher_order_macro.exs at master · 4ait/bin_struct · GitHub

Ridtt

Ridtt

I like current way of how automatic doc tests are works. Can I achieve same with separate .md’s?

What is actually special formatting?

Some reasoning:

My main goal is to keep things as they are. Simple things will be simple anyway. Hard things will be hard anyway. I don’t see reason in any shortcuts for example for situation where some length is may be directly inferred from other field like most libraries I saw do. Every attempt of introducing multi-field writer or any other complex behavior has failed.

I ended up with idea of dynamic behavior is always a callback, there is a single way of creating virtual field during parse/decode using read_by and single way of creating anything in new context automatically using builder. I got so far with idea not mixing things and I liked it. Once you have finished writing your declaration and tested it you anyway can forget about what is inside. You have guarantees if it is working alone it will be working same inside any tree. Good way to reuse parts is to compose it into higher order macro, which again don’t care of presence of shortcuts and so on.

Asd

Asd

I meant the code formatting. Default is mix format. You use something different, with newlines before end and after def and a lot of other things. I just noted that. I love reading code with uncommon styles of formatting and uncommon approaches to writing code :heart:

In my projects I just copy-paste the examples into tests. Doctests are good, but these modules will be loaded in every system using the library and this may be a problem for embedded devices (which are top users of the binary protocols)

And this also brings me to another question. Since this library is generating parsers, is it possible to use it with runtime: false?

Where Next?

Popular in Libraries Top

seancribbs
Today I released a new dialyzer Mix task as the dialyzex package! At the time we started writing this task, the existing dialyzer integra...
New
woylie
I released Doggo, a collection of unstyled Phoenix components. Features Unstyled Phoenix components. Storybook that can be added to...
New
arkgil
Hi all! I’m happy to announce that Telemetry v0.3.0 is out! This release marks the conversion from Elixir to Erlang so that all the libr...
New
pkrawat1
Presenting Aviacommerce, open source e-commerce platform in Elixir Aviacommerce is an open source e-commerce platform in Elixir. We at...
New
achempion
Hi, I would like to tell about my initiative to further maintain and develop Waffle project which is the fork of Arc library. The progre...
New
Antrater
Hi everyone! I’m thrilled to announce a huge thing. We have been developing Elixir Moon Design System for quite a while. We are finally ...
New
engineeringdept
I’ve just released the first version of Snap, an Elasticsearch client. It borrows ideas about application structure and process managemen...
New
bryanjos
Hi, I just published version 0.23.0 of Elixirscript. Most of the changes are around JavaScript interop now that Elixirscript uses the ...
New
Hal9000
Here is my first stab at this. README pasted below. https://github.com/Hal9000/elixir_random Comments and critiques are welcome. Th...
New
maltoe
Hello! Came here to announce ChromicPDF, a pet project PDF generator I’ve been working on for the past few months. Why another PDF gener...
New

Other popular topics Top

JDanielMartinez
Hi! May someone helps me, please! I have two apps into an umbrella project: the first one is Database, which manages queries, and the se...
New
peerreynders
Manning 2016 Halloween weekend sale via Deal of the Day Friday, October 28 - Half off all MEAPs - code WM102816LT Saturday, October 29 ...
326 29600 154
New
sorentwo
Hello! tl;dr Announcing Oban, an Ecto based job processing library with a focus on reliability and historical observability. After spen...
977 41022 311
New
vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New
lessless
I believe there are people here who are dealing with CSV files import on the daily basis, and since Excel is a really popular tool there ...
New
grych
Hi folks, Few months ago I have announced the proof-of-concept of the library to manipulate the browsers DOM objects directly from Elixi...
639 49522 488
New
vertexbuffer
Hello, can anybody help here..? I have a list of players and I what to delete an element, but every for loop the list is reverting to ori...
New
alice
Hey, Just curious what are the main benefits of Elixir compared to Clojure? When is Elixir more useful than Clojure and vice versa? Th...
New
vonH
When I run the Plug and I recompile I wind up having to use Ctrl C to quit iex and start again. Witht the help of rlwrap I can use the cu...
New
AstonJ
We’ve put together this wiki for Phoenix LiveView - please feel free to add any info you feel is worth including. What is Phoenix LiveV...
New

Sub Categories:

We're in Beta

About us Mission Statement