| Crates.io | kroos |
| lib.rs | kroos |
| version | 0.0.3 |
| created_at | 2025-06-04 00:45:57.704071+00 |
| updated_at | 2025-06-08 01:54:57.281149+00 |
| description | Flexible smart pointer for ?Sized types with customizable reference counting. |
| homepage | |
| repository | https://github.com/Pavez7274/kroos |
| max_upload_size | |
| id | 1699667 |
| size | 65,545 |
A zero-cost abstraction over heap allocation and reference counting for ?Sized types, built for performance-critical systems with manual memory control.
While developing kodrst, we encountered non-trivial overhead from using Arc<str> in performance-sensitive paths. Even in immutable scenarios, Arc introduces unnecessary synchronization and layout complexity. kroos provides low-level primitives (Flake, Rime) designed to bypass these costs entirely, trading away safety and general-purpose semantics in favor of raw performance.
kroosstr or [u8] on the heap with no overhead.Arc or Rc.Drop, aliasing, or race conditions).Your types require Drop, or have complex ownership semantics.
You want ergonomic or type-safe abstractions.
You're not prepared to work at the raw pointer level.
Flake<T: ?Sized>A Box-like wrapper for unsized types without ownership semantics. It provides heap allocation for types like str, [u8], or any ?Sized data using raw pointers.
Flake::newAllocates a copy of a referenced ?Sized object to the heap.
let flake = Flake::new("hello");
assert_eq!(&*flake, "hello");
Flake::stealMoves a Sized value directly into the heap, without duplication.
let flake = Flake::steal(String::from("hi"));
assert_eq!(&*flake, "hi");
A Flake<T> contains a single fat pointer to the heap-allocated value. It does not store length or capacity explicitly — metadata is embedded in the fat pointer.
While Flake is primarily intended for immutable data, controlled mutation is allowed:
let mut flake = Flake::new(&[1, 2, 3][..]);
unsafe { (*flake.as_mut_ptr())[0] = 42; }
assert_eq!(&*flake, &[42, 2, 3]);
[!CAUTION] Use only if you guarantee exclusive access.
Rime<C, T: ?Sized>A compact, inline, reference-counted pointer to unsized types, using a custom Counter.
Rime stores [ Counter | Data ] in one allocation.str, [u8], etc.)Rime<AtomicU8, str> or Rime<Cell<u8>, str>.Rime::newCopies a reference to heap and initializes a new refcount.
let rime = Rime::<AtomicU8, str>::new("hello");
let clone = rime.clone();
assert_eq!(&*clone, "hello");
Rime::stealTakes ownership of a Sized value and moves it into a single block.
let rime = Rime::<u8, String>::steal("hi".to_string());
assert_eq!(&*rime, "hi".to_string());
If you use a Cell<u8> counter (or similar interior-mutable strategy), you can achieve safe mutability under the following constraints:
Rime must not be cloned (i.e. you must be the only owner).
Use as_mut_ptr to mutate contents in-place.
You are responsible for enforcing Rust’s aliasing rules manually.
You can store metadata in a header format like:
[ Counter | Metadata | Data ]
↑ ↑
len/cap T
For example, a fixed-capacity string-like object might store len as a usize, followed by a zero-terminated buffer. On mutation:
len manually.This enables a fixed-capacity "string" stored inline, avoiding reallocation.
Flake::from_raw and Rime::from_raw allow you to construct heap-backed references without the intermediate cost of stack allocation followed by a move. Instead of creating a Box<T> or Vec<T> and extracting its pointer, you can allocate memory and write directly into it.
For example, to create a *mut str without touching the stack:
pub unsafe fn alloc_str_and_write(bytes: &[u8]) -> *mut str {
let len = bytes.len();
let ptr = alloc(Layout::array::<u8>(len).unwrap());
// You can handle layouts errors checking ptr.in_null()
ptr.copy_from_nonoverlapping(bytes.as_ptr(), len);
ptr::slice_from_raw_parts_mut(ptr, len) as *mut str
}
You can then wrap the result into a Flake:
let raw = unsafe { alloc_str_and_write(b"hola") };
let flake = unsafe { Flake::from_raw(raw) };
This pattern avoids temporary allocations or extra indirection like:
let boxed = Box::new(value); // Allocates and moves
let flake = Flake::new(&*boxed); // Copies again
Instead, you allocate once and write directly where the value will live.
[!NOTE] Box is optimized for sometimes do a
placement-in protocol-like.
| Feature | Box / Arc |
Flake / Rime |
|---|---|---|
Works with ?Sized |
✅ (Box) |
✅ |
| Custom counter | ❌ | ✅ |
| Atomic optional | ✅ (Arc) |
✅ (via AtomicU*) |
| Inline allocation | ❌ | ✅ |
| Copy or move control | ❌ | ✅ |
| Drop safety | ✅ | ❌ (must be manual) |
Flake and Rime bypass Drop, Clone, and Rust's ownership model.Copy fields.Ensuring uniqueness or correct refcounting.
Avoiding data races (use Atomic* if needed).
Deallocating correctly (done automatically, but only if created via safe constructors).
This project is licensed under the GNU AGPL v3.
All contributions must retain clear attribution to the original author(s).
If you modify or extend this project, please include appropriate credit in your repository and documentation.
In particular:
I believe in free software and shared credit.
This crate exists to explore precision memory control and support edge-case optimizations in projects like kodrst. It is not intended as a general-purpose utility. If you're using Flake or Rime outside internal systems, you're either very brave — or very desperate...