| Crates.io | viffy |
| lib.rs | viffy |
| version | 0.1.5 |
| created_at | 2025-06-10 23:53:09.343807+00 |
| updated_at | 2025-06-11 00:44:48.853665+00 |
| description | SoA + SIMD automata generator |
| homepage | |
| repository | https://codeberg.org/Esther1024/viffy |
| max_upload_size | |
| id | 1707874 |
| size | 112,117 |
viffy (vee-fee) is an Ergonomic SIMD library + automata generator for SoA databases.
Rust does not expect multiple threads to write to the same section of memory blocks.
Viffy relies on that memory model. This is why it's YOUR responsability to appropriatedly manage thread lifetimes. A cell provided is ShareableCell<T>, this cell is entirely unsound with the normal Rust memory model and provides multiple mutable references to the same memory block.
Viffy provides it's subset of mathematical operations in math.rs, those are just dumb versions of the structures/operations - it's recommended to use nalgebra in place (and implement the serializer/deserializer if you need it). Since viffy does not seek to be a mathematical library.
Those shall only be used if you want a quick "test" of viffy itself.
A small SoA database generator is also provided, there is basic support for some relational linkage between objects.
On your build.rs you can quickly implement the generator by adding viffy as a build-dependency:
Cargo.toml:
[build-dependencies]
viffy = { git = "https://codeberg.org/Esther1024/viffy" }
build.rs:
fn main() -> Result<(), Box<dyn std::error::Error>> {
viffy::generator::write_file("./src/db.txt", "db_generated.rs")?;
println!("cargo::rerun-if-changed=src/db.txt");
println!("cargo::rerun-if-changed=build.rs");
Ok(())
}
src/db.txt:
@global = {
seed = u32
}
CarType = {
@serde = scenario
@size = 4096
velocity = f32
speed = f32
traction = f32
}
src/db.rs:
include!(concat!(env!("OUT_DIR"), "/db_generated.rs"));
src/main.rs:
pub mod db;
pub struct State {
pub world: viffy::ShareableCell<db::World>,
}
impl State {
pub fn new_in_heap() -> Arc<Self> {
unsafe {
let mut state = Arc::<Self>::new_uninit();
let ptr = Arc::get_mut(&mut state).unwrap().as_mut_ptr();
let world_ptr = ptr.wrapping_byte_add(core::mem::offset_of!(Self, world)) as *mut viffy::ShareableCell<db::World>;
let uninit_world = world_ptr.as_mut().unwrap().borrow_mut();
uninit_world.write_new();
state.assume_init()
}
}
}
pub fn main() {
let my_state = State::new_in_heap();
println!("hello viffy world!");
}
Object = {
<property> = <type>
}
Special properties can be defined with the @ prefix:
@size: Define the size (in integer quantities) of the object.@serde: Define the default serialization and deserialization target for this object (unless overriden in a given property).@relation: Define this as a relation as opposed to an object.@first: First object member of the relation, for example Country.@firstName: Canonical name of the first member of the relation, for example, owner.@second: Second object member of the relation, for example Province.@secondName: Canonical name of the second member of the relation, for example, province.@global: A group defined as global will have it's members be accessed via the get_global_member and set_global_member functions. Only one member exists at a time, as opposed to the SoA paradigm present on other objects.@adjacency: Marks this group as an adjacency descriptor, only a triangular N by M matrix will be generated, no members shall be added to this group@adjType: The base type to use for this adjacency descriptor.@unilateral: Generate a relation that can be (i,j) != (j,i)@bilateral: Generate a relation such that (i,j) == (j,i)@erasable: Group is erasable as opposed to a fixed size, implementation uses a freelist to keep track of "dead" objects.In the cases given above for @first and @second, we define something akin to a ProvinceOwnership relation, which has one possible key (first key), and many possible links (second key).
If the property name starts with [borrow], the functions will be generated such as the property is regarded to be more efficient being borrowed than being copied (such as arrays).
The generated world containers guarantee immutable access, that is, a &mut World isn't required to change a given property within the world, this property allows to independently modify containers, and - optionally, allow to use mutexes to detect data races in debug builds. It is also primordial to allow near-zero-cost reads and writes without compromising code readability.
The main functions for non-borrowed members:
set_object_property(&self, id: ObjectId, v: T);: Sets the given object, notice how World doesn't need to be mutable.get_object_property(&self, id: ObjectId) -> T;: Gets the given objectfor_each_object(&self, f: F);: Iterate over all objects.for_each_mut_object(&self, f: F);: Iterate over all objects (mutably).parallel_for_each_object(&self, f: F);: Iterate over all objects but in parallel (uses Rayon).ranged_parallel_for_each_object(&self, steps: usize, at_step: usize, f: F);: Iterate over all objects but only the ones in range, and divide them in pairs of steps, in parallel (uses Rayon).vector_serial_for_each_object(&self, f: F);: Iterate over all objects, however, insert every one of them in serial, such as that the item list is populated with [n0, n0, n0, n0]vector_parallel_for_each_object(&self, f: F);: Iterate over all objects, however, insert every one of them in parallel, such as the item list is populated with [n0, n1, n2, n3]iter_object(&self);: Returns an iterator to all objects.parallel_iter_object(&self);: Returns a parallel iterator to all objects.The main functions for borrowed members:
get_object_property<F>(&self, id: ObjectId, f: F) -> T;: Gets the given object, and performs a mutable operation upon it, because f takes a closure with a reference to said object.Main functions of adjacencies:
pub fn set_province_adjacency_for_province(&self, id: ProvinceId, v: ProvinceId);: Set as adjacent.
pub fn clear_province_adjacency_for_province(&self, id: ProvinceId, v: ProvinceId);: Clear adjacency.
pub fn get_province_adjacency_for_province(&self, id: ProvinceId, v: ProvinceId) -> bool;: Query adjacency.
pub fn for_each_province_adjacency_in_province<F: FnMut(&Self, ProvinceId)>(&self, id: ProvinceId, mut f: F);: Iterate over adjacencies in a province (with the lambda called over the adjacent objects).