.github/workflows | ||
src | ||
.gitignore | ||
Cargo.toml | ||
LICENSE-APACHE | ||
LICENSE-MIT | ||
README.md |
fist_bump
A customizable bump allocator.
Note that this crate currently requires nightly rust, as it uses the allocator API.
Usage
Add the following to your Cargo.toml
:
[dependencies]
fist_bump = "0.1"
FistBump
doesn't directly implement Allocator
, however &FistBump
does, so that
anything that uses it can only live as long as it does.
Anything that is generic over an allocator can be used in a FistBump
, such as Vec<T>
or Box<T>
Example:
let fistbump = FistBump::<OneUse<ArrayBuffer<64>>>::new();
let mut vec = Vec::new_in(&fistbump);
vec.extend_from_slice(b"Hello");
vec.extend_from_slice(b", world!");
assert_eq!(&vec, b"Hello, world!");
let boxed = Box::new_in((1..=100).sum(), &fistbump);
assert_eq!(boxed, 5050);
(currently this example isn't tested because it triggers an ICE)
Customization
FistBump
uses generics to let you choose what functionality you want from it.
Buffers
A fistbump is always backed by a buffer of bytes, which is where it stores its contents.
ArrayBuffer
ArrayBuffer
lives on the stack, and has a constant size.
It is generic over its size, and is basically a [u8; N]
.
#![feature(allocator_api)]
use fist_bump::{FistBump, OneUse, ArrayBuffer};
let fistbump = FistBump::<OneUse<ArrayBuffer<16>>>::new();
// You should prefer using `with_capacity_in` rather than `new_in`
// as re-allocating repeatedly wastes a lot of space
let mut vec = Vec::with_capacity_in(4, &fistbump);
vec.push(15u32);
vec.push(15493u32);
vec.push(62u32);
vec.push(150000u32);
DynamicBuffer
DynamicBuffer
lives inside another allocator, and can be resized.
Its size is determined at runtime, and is basically a Vec<u8>
#![feature(allocator_api)]
use fist_bump::{FistBump, OneUse, DynamicBuffer};
let mut fistbump = FistBump::<OneUse<DynamicBuffer>>::with_size(16);
let mut vec1 = Vec::with_capacity_in(4, &fistbump);
vec1.push(42);
vec1.push(3141);
vec1.push(151);
vec1.push(11);
// Need to drop the vec, see below
drop(vec1);
fistbump.resize(24);
let mut vec2 = Vec::with_capacity_in(8, &fistbump);
vec2.extend_from_slice(b"rustlang");
assert_eq!(vec2, b"rustlang");
The buffer cannot be resized while there are outstanding allocations, since the pointers into it may be invalidated.
# #![feature(allocator_api)]
# use fist_bump::{FistBump, OneUse, DynamicBuffer};
let mut fistbump = FistBump::<OneUse<DynamicBuffer>>::with_size(4);
let mut vec = Vec::with_capacity_in(4, &fistbump);
vec.extend_from_slice(b"heyo");
fistbump.resize(8);
assert_eq!(vec, b"heyo");
Reusability
Reusable
and OneUse
are wrappers around a buffer that indicate whether the buffer can be used more than once or not.
[OneUse
]
[Reusable
]
Takes a mutable reference to a buffer, allowing it to be used more than once.
Has to take a mutable reference, or else multiple FistBump
s could use it at once, overwriting each other.
#![feature(allocator_api)]
use fist_bump::{FistBump, ArrayBuffer};
let mut buffer = ArrayBuffer::<16>::new();
{
let fistbump_a = FistBump::in_buffer(&mut buffer);
// do some stuff
}
// At this point, `fistbump_a` has gone out of scope, and so the
// buffer can now be re-used.
{
let fistbump_b = FistBump::in_buffer(&mut buffer);
// do some other stuff
}
It's better to use a single Reusable
, rather than repeatedly creating OneUse
s.
For example, instead of doing this:
for i in 0..1000 {
let fistbump = FistBump::<OneUse<ArrayBuffer<64>>>::new();
// do stuff with it...
}
Do this:
let mut buffer = ArrayBuffer::<64>::new();
for i in 0..1000 {
let fistbump = FistBump::in_buffer(&mut buffer);
// do stuff with it...
}
Thread Safety
By default, FistBump
is not thread safe. However, you can enable thread-safety by using the ThreadSafe
flag,
i.e. FistBump<_, ThreadSafe>
. Additionally, you can explicitly set it to being thread-local with FistBump<_, ThreadLocal>
.
If you don't need to use it across threads, leave it set to ThreadLocal
, as that is likely to be more efficient.
Also note that even if it is set to ThreadLocal
, a FistBump
can still be sent across threads, just not shared.
This is okay because sending it requires there be no references to it, and thus no allocations.
Usage in static
s
When using a FistBump
in a static, several restrictions apply:
- It must be
OneUse
. - It must use an
ArrayBuffer
. - It must be
ThreadSafe
.
These restrictions are represented by the type FistBump<OneUse<ArrayBuffer<_>>, ThreadSafe>
License
This project is licensed under either of
at your option.