capnp: adding capnp::serialize::NoAllocSliceSegments and capnp::serialize::read_message_from_flat_slice_no_alloc #276
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
I'm adding an implementation for parsing unpacked capnp message which does not require heap allocations on happy path (only for error message if error occurs).
Context
no alloc support - #221
Currently all existing options for parsing capnp message require heap allocations.
For example, even
SliceSegments
will do heap allocation for storing segment offsets inside theVec
:This is a problem for embedded applications where memory is scarce and heap allocations are either impossible or strongly discouraged, and having ability to parse message without doing heap allocations will improve experience of embedded developers using capnproto-rust.
In proposed implementation
alloc
crate is still required due to use ofString
incapnp::Error
, however there will be no allocation happening on happy path, only in case of errors.Implementation details
The header of the capnp message has only list of segment sizes. Offsets of segments can be calculated by combining segment sizes which are coming before segment of interest. Because of this, noalloc implementation has to parse at least first N segment info in the header every time
NoAllocSliceSegments::get_segment
is called. This is a performance downside of using noalloc implementaiton.To partially mitigate the performance issue, I implemented a special case for messages with a single segment - they go through a separate enum variant and won't require message parsing on
NoAllocSliceSegments::get_segment
.Proposed implementation works only for messages which are serialized in "non-packed" format. Packed messages would need to be unpacked first (which requires heap allocation) and it's something that need to be addressed additionally.
Considerations regarding reuse of
read_segment_table
I attempted to reuse read_segment_table method in my implementation. Unfortunately it has heap allocation inside it:
Resulting
SegmentLengthsBuilder
hasVec
inside it:it also parses all segments, when in my case it's desired to only parse first N segments.
I tried to make logic work for alloc and noalloc cases through injected traits, but it become too cumbersome, I realized that benefit of reusing that method outweighs increased complexity of the logic, so I decided to implement all logic from scratch.
Random number generation
I wanted to have a random number generator for generating test inputs. I introduced very naive random number generator, I was not sure if it's desired to have a build time dependency on
rand
crate, but I'll be happy to make a change and take the dependency on it if it's your preference.