https://github.com/virxec/combo_vec
A library for creating a "combo stack array-heap vector", or simply a re-sizable array
https://github.com/virxec/combo_vec
data-structures rust stack
Last synced: 6 months ago
JSON representation
A library for creating a "combo stack array-heap vector", or simply a re-sizable array
- Host: GitHub
- URL: https://github.com/virxec/combo_vec
- Owner: VirxEC
- Created: 2022-09-15T21:51:08.000Z (about 3 years ago)
- Default Branch: main
- Last Pushed: 2024-03-24T00:35:46.000Z (over 1 year ago)
- Last Synced: 2024-04-25T10:21:37.087Z (over 1 year ago)
- Topics: data-structures, rust, stack
- Language: Rust
- Homepage: https://crates.io/crates/combo_vec
- Size: 71.3 KB
- Stars: 2
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# combo_vec
[](https://github.com/rust-secure-code/safety-dance/)
`ComboVec` is for creating a "combo stack array-heap vector", or simply a resizable array with a vector for extra allocations.
Not only that, but this library has `ReArr` if you just want the resizable array part!
Create a new `ComboVec` with the `combo_vec!` macro and a new `ReArr` with the `re_arr!` macro.
This works by allocating an array of `T` on the stack, and then using a Vec on the heap for overflow.
The stack-allocated array is always used to store the first `N` elements, even when the array is resized.
_No_ `Default`, `Copy`, or `Clone` traits are required for `T` at all;
but if T does implement any of them, then `ComboVec` and `ReArr` will also implement them.
This also applied to `PartialEq`, `PartialOrd`, `Eq`, `Ord`, `Hash`, `Debug`, and `Display`.## Why use ComboVec
This is mostly used for when you know the maximum number of elements that will be stored 99% if the time, but don't want to cause errors in the last 1% and also won't want to give up on the performance of using the stack instead of the heap most of the time.
I've gotten performance bumps with `ComboVec` over the similar type SmallVec (both with and without it's `union` feature.)
In a test of pushing 2048 (pre-allocated) elements, almost a 54% performance increase is shown:
- `ComboVec`: 4.54 µs
- `SmallVec`: 9.33 µsThe `combo_vec!` macro is very nice and convenient to use even in const contexts.
```rust
use combo_vec::{combo_vec, ComboVec};const SOME_ITEMS: ComboVec = combo_vec![1, 2, 3];
const MANY_ITEMS: ComboVec = combo_vec![5; 90];
const EXTRA_ITEMS: ComboVec<&str, 5> = combo_vec!["Hello", "world", "!"; None, None];// Infer the type and size of the ComboVec
const NO_STACK_F32: ComboVec = combo_vec![];// No const-initialization is needed to create a ComboVec with allocated elements on the stack
use std::collections::HashMap;
const EMPTY_HASHMAP_ALLOC: ComboVec, 3> = combo_vec![];// Creating a new ComboVec at compile time and doing this does have performance benefits
let my_combo_vec = EMPTY_HASHMAP_ALLOC;
````ComboVec` also implements many methods that are exclusive to `Vec` such as `extend`, `truncate`, `push`, `join` etc.
## Why use ReArr
In a test of pushing 2048 (pre-allocated) elements, it ties for performance with `ArrayVec`:
- `ReArr`: 4.07 µs
- `ArrayVec`: 4.00 µsThe `re_arr!` macro is very nice and convenient to use even in const contexts.
```rust
use combo_vec::{re_arr, ReArr};const SOME_ITEMS: ReArr = re_arr![1, 2, 3];
const MANY_ITEMS: ReArr = re_arr![5; 90];
const EXTRA_ITEMS: ReArr<&str, 5> = re_arr!["Hello", "world", "!"; None, None];// Infer the type and size of the ReArr
const NO_STACK_F32: ReArr = re_arr![];// No const-initialization is needed to create a ComboVec with allocated elements on the stack
use std::collections::HashMap;
const EMPTY_HASHMAP_ALLOC: ReArr, 3> = re_arr![];// Creating a new ReArr at compile time and doing this does have performance benefits
let my_re_arr = EMPTY_HASHMAP_ALLOC;
````ReArr` also implements many methods that are exclusive to `Vec` such as `extend`, `truncate`, `push`, `join` etc.
## Examples
A quick look at a basic example and some methods that are available:
```rust
use combo_vec::combo_vec;let mut combo_vec = combo_vec![1, 2, 3];
// Allocate an extra element on the heap
combo_vec.push(4);
// Truncate to a length of 2
combo_vec.truncate(2);
// Fill the last element on the stack, then allocate the next items on the heap
combo_vec.extend([3, 4, 5]);
```### Allocating empty memory on the stack
You can allocate memory on the stack for later use without settings values to them!
No Copy or Default traits required.
```rust
use combo_vec::{ComboVec, ReArr};// Allocate a new space to store 17 elements on the stack.
let empty_combo_vec = ComboVec::::new();
let empty_re_arr = ReArr::::new();
```### Allocating memory on the stack in const contexts
The main benefit of using the `combo_vec!`/`re_arr!` macros is that everything it does can be used in const contexts.
This allows you to allocate a ComboVec at the start of your program in a Mutex or RwLock, and have minimal runtime overhead.
```rust
use combo_vec::{combo_vec, ComboVec, re_arr, ReArr};// Create a global variable for the various program states for a semi-unspecified length
use std::{collections::HashMap, sync::RwLock};
static PROGRAM_STATES: RwLock, 20>> = RwLock::new(combo_vec![]);// If we know the stack will never be larger than 20 elements,
// we can get a performance boost by using ReArr instead of ComboVec
let mut runtime_stack = ReArr::::new();
```### Go fast with const & copy
We can take advantage of `ComboVec` and `ReArr` by creating one const context then copying it to our runtime variable.
This is much faster than creating a new `ComboVec` at runtime, and `T` does _not_ need to be `Copy`.Here's a basic look at what this looks like:
```rust
use combo_vec::{combo_vec, ComboVec};const SOME_ITEMS: ComboVec = combo_vec![];
for _ in 0..50 {
let mut empty_combo_vec = SOME_ITEMS;
empty_combo_vec.push("Hello".to_string());
empty_combo_vec.push("world".to_string());
println!("{}!", empty_combo_vec.join(" "));
}
```