-
Notifications
You must be signed in to change notification settings - Fork 3.4k
Description
I'd like to use a FlatBufferBuilder directly into an externally-allocated buffer from Rust.
Some context: I'm looking to implement the equivalent of this C++ API. It's an interface to a shared memory IPC system which builds a table in a region of shared memory, before handing off ownership to other processes (after the table is completed).
The C++ version has a flatbuffers::Allocator
implementation which aborts if you do anything besides a single allocate
call. Using the initial_size
argument to FlatBufferBuilder
's constructor results in the FlatBufferBuilder
allocating that single piece of memory and then filling it in, and if you try building a table that's too large it aborts in the custom Allocator
implementation with an error message pointing to the size that needs to be increased.
This is part of what's needed for #7005.
Proposed API:
/// TODO: Does the memory need to be zeroed?
trait Allocator: Index<SliceIndex<[u8]>> + IndexMut<SliceIndex<[u8]>> {
/// Grows the buffer, with the old contents being moved to the end.
///
/// Returns the amount it has grown by, so the caller can update offsets.
fn grow_downwards(&mut self) -> usize;
fn len(&self) -> usize;
}
struct DefaultAllocator {
owned_buf: Vec<u8>,
}
/// len, Index, and IndexMut forward to owned_buf.
/// grow_downwards is the current FlatBufferBuilder::grown_owned_buf, except the self.used_space()
/// and self.head operations which stay in grow_owned_buf.
pub struct FlatBufferBuilder<A: Allocator, 'fbb> {
buf: A,
<everything else the same>
}
/// Not sure if this works directly, or if it needs a marker trait that only DefaultAllocator implements.
impl<A: DefaultAllocator, 'fbb> FlatBufferBuilder<A, 'fbb> {
pub fn with_capacity(size: usize) -> Self {
Self::with_capacity_and_allocator(size, DefaultAllocator::new())
}
/// `new` moves here too.
}
A more direction translation of the C++ API would be something like this:
enum SomeAllocator {
Default(Vec<u8>),
Dyn(Box<dyn Allocator>),
}
but I don't think that makes sense in Rust. Rust's generics are easier to work with than C++, so doing it with the trait directly means users can have a custom allocator without any vtables if desired. It also allows the custom type to carry lifetimes with it, which I want for my use case.
I'm prepared to implement this and send a PR. Thoughts on the API before I start doing that?