We previously defined a @types map that was used to generate function
matches for each known type. Because we only use @types for that one
purpose, it's simpler and more readable to just write out the function
heads explicitly.
This change also renames `int_type` to `type_id` for consistency with
the typespec name.
This eliminates the private bool_to_int/1 function and lets us return
constant binary result values directly.
We also strictly match on just the true and false atoms.
There is a nominal performance improvement, but nothing significant.
We were previously generating private copies of the "skip" functions in
each generated struct module. These functions are generic and have been
moved to the top-level Binary protocol module instead. The reduces the
size of the generated modules by a nice amount.
We only skip unrecognized fields so this code shouldn't be called very
often. In practice, however, we can't predict how often this case will
arise, so the performance of these function is important. Fortunately,
this change doesn't introduce a measurable performance cost.
Lastly, this change adds Binary module documentation and some initial
typespecs.
The changes to ParserUtils were fumbled and broke BinaryProtocolBenchmark,
which is the more valuable benchmark. UnionSerializationBenchmark was used to
check the performance impact of a particular commit and isn't really worth
maintaining.
* Behaviour module generation
For the server side, I imagine that the user would specify a handler
module and pass it into the service. Then it would be called with the
appropriate arguments.
I thought it might be nice to have a behaviour for these modules that
users could be implement.
Behaviours here are fully typed with the correct success types.
* Improve iolist optimization
The new function `optimize_iolist` combines the old `merge_binaries` and
`simplify_iolist` functions. It also improves on them slightly, for instance by
terminating iolists with a binary instead of the empty list, and by more
consistently combining adjacent binaries.
* Document iolist optimization steps
It was producing a redundant case statement.
case foo do
nil -> <<>>
_ ->
[<<2, id::size(16)>>, case foo do
nil -> <<0>>
false -> <<0>>
_ -> <<1>>
end]
end
The new output is simpler, both in the number of case statements and in the
structure of the iolist returned.
case foo do
nil -> <<>>
false -> <<2, id::size(16), 0>>
_ -> <<2, id::size(16), 1>>
end
* More robust handling of void return types
The erlang server replies to void responses with <<12, 0, 0, 0, 0>>
which is an empty struct and the stop byte from the respone
struct. Other implementations just reply with the stop byte from the
response struct. We can now parse both.
I also fixed a bug that would occur if the sequence ids or messages
would mismatch. I added tests for all cases.
* Formatting is more consistent
* Match unions with zero or one field set
This uses matching in the `serialize` definition to enforce that only unions
with zero or one field can be serialized.
This moves a bit of complexity from struct_deserializer into
value_serializer(:bool). Things are a little more coherent overall, and it also
helps simplify struct_deserializer, which is getting complex.
* Generated Thrift client
This PR is the start of an auto-generated Framed Thrift Binary Protocol
client.
Some work was needed in order to make the client work. This included
modifying the existing generators to:
* Properly handle void return types
* Create exceptions using defexception rather than defstruct.
I also modified the thrift test case so it was able to pass context from
setup functions. This is necessary because testing is accomplished by
creating an erlang server off of a test client and running actual thrift
calls against it. I've validated a number of different scenarios, and
all appear to work
* Added generation for service structs
Thrift models RPC function arguments and responses as structs. This adds
generation for struct args and responses
* Addressed PR comments
* Trying to get CI to pass
* Skip unknown fields
If you have an old version of a schema, and are sent a message with
fields from a newer version, you should ignore them and continue
processing.
The old behavior was to crash, which causes problems in production
systems
* Moved binary matches to the function head
* PR fixes
This gives the user more fine grained control over which .thrift files are
processed. For instance, a directory might contain many files but you only want
one or a few.
A pure Elixir implementation of the Thrift binary protocol.
This PR includes serialization and deserialization for all thrift types as well as testing to make sure we match the thrift spec (which can be found here: https://erikvanoosten.github.io/thrift-missing-specification/#_binary_encoding)
The binary protocol is generated alongside the thrift data types, yielding a much faster implementation than the apache project's canonical one.
Our benchmarks (using benchfella) indicate a speedup of between 10 and 20x.
## Binary Protocol Performance
benchmark name | iterations | average time
------------------|-----------|---------------
elixir serialization (left as IOList) | 2000 | 849.01 µs/op
elixir serialization (iolist_size) | 2000 | 933.83 µs/op
elixir serialization (converted to binary) | 1000 | 1281.23 µs/op
elixir deserialization | 1000 | 1178.46 µs/op
erlang serialization left as IOList | 100 | 10284.84 µs/op
erlang serialization (converted to binary) | 100 | 11588.89 µs/op
erlang deserialization | 100 | 21433.17 µs/op