avila_atom/lib.rs
1//! # avila-atom
2//!
3//! **Atomic Computational Structures - Fundamental Data Structures**
4//!
5//! This library implements core data structures built from first principles,
6//! providing type-safe primitives for systems programming with zero-compromise
7//! performance characteristics.
8//!
9//! ## Available Structures
10//!
11//! - `Option<T>` - Optional value (presence/absence) - zero-cost abstraction
12//! - `Result<T, E>` - Result type (success/failure) - enum-based sum type
13//! - `DynamicArray<T>` - Contiguous growable array with amortized O(1) push
14//! - `AssociativeArray<K, V>` - Hash-based or tree-based key-value store
15//! - `StringBuffer` - UTF-8 encoded string with growable capacity
16//!
17//! ## Philosophy
18//!
19//! These structures are atomic computational primitives - stable elements
20//! that compose infinitely to build complex software systems.
21//!
22//! ### Performance Characteristics
23//!
24//! - **Zero-cost abstractions**: No runtime overhead vs manual implementation
25//! - **Memory locality**: Contiguous allocation for cache efficiency
26//! - **Compile-time optimization**: Monomorphization enables aggressive inlining
27//! - **Stack-preferred**: Structures optimize for stack allocation when possible
28//!
29//! ### no_std Compatibility
30//!
31//! All structures work in `no_std` environments with `alloc` feature:
32//! - `AssociativeArray` falls back to B-Tree (O(log n)) instead of HashMap (O(1))
33//! - Memory allocation via global allocator trait
34//! - Zero dependency on operating system primitives
35
36#![cfg_attr(not(feature = "std"), no_std)]
37#![warn(missing_docs)]
38
39#[cfg(feature = "std")]
40extern crate std;
41
42#[cfg(not(feature = "std"))]
43extern crate alloc;
44
45#[cfg(not(feature = "std"))]
46use alloc::{vec::Vec, string::String, collections::BTreeMap};
47
48#[cfg(feature = "std")]
49use std::{vec::Vec, string::String, collections::HashMap};
50
51/// Optional value type - represents presence or absence of a value
52///
53/// **Memory layout**: Single byte discriminant + value (if Some)
54/// **Size**: `size_of::<T>() + 1` (optimized to `size_of::<T>()` for nullable pointers)
55/// **Performance**: Zero-cost - compiles to same code as manual null checks
56pub use core::option::Option;
57
58/// Result type - represents success (Ok) or failure (Err)
59///
60/// **Memory layout**: Tagged union (discriminant + larger of Ok/Err)
61/// **Size**: `1 + max(size_of::<T>(), size_of::<E>())`
62/// **Performance**: Zero-cost abstraction, optimal enum representation
63pub use core::result::Result;
64
65/// Dynamic array with contiguous memory layout
66///
67/// **Complexity**:
68/// - Access: O(1) by index
69/// - Push: O(1) amortized (doubles capacity on realloc)
70/// - Insert/Remove: O(n) for arbitrary position
71///
72/// **Memory**: `ptr + len + capacity` (24 bytes on 64-bit)
73/// **Growth strategy**: Geometric progression (2x) for amortized O(1)
74#[cfg(feature = "std")]
75pub type DynamicArray<T> = Vec<T>;
76
77#[cfg(not(feature = "std"))]
78pub type DynamicArray<T> = Vec<T>;
79
80/// Hash map (std) or B-Tree map (no_std) for key-value storage
81///
82/// **std mode** (HashMap):
83/// - Lookup: O(1) average, O(n) worst case
84/// - Insert: O(1) amortized
85/// - Hasher: SipHash 1-3 (cryptographic, DoS resistant)
86/// - Load factor: Grows at 90% capacity
87///
88/// **no_std mode** (BTreeMap):
89/// - Lookup: O(log n)
90/// - Insert: O(log n)
91/// - Node size: Optimized for cache lines
92/// - Ordering: Requires `K: Ord`
93#[cfg(feature = "std")]
94pub type AssociativeArray<K, V> = HashMap<K, V>;
95
96#[cfg(not(feature = "std"))]
97pub type AssociativeArray<K, V> = BTreeMap<K, V>;
98
99/// UTF-8 encoded string buffer
100///
101/// **Guarantees**:
102/// - Valid UTF-8 at all times (invariant enforced by type system)
103/// - Contiguous memory layout
104/// - Growable capacity with geometric progression
105///
106/// **Memory**: `ptr + len + capacity` (24 bytes on 64-bit)
107/// **Validation**: All mutations validate UTF-8 boundaries
108pub type StringBuffer = String;
109
110/// Library version constant
111pub const VERSION: &str = env!("CARGO_PKG_VERSION");
112
113/// Compile-time size constants for common types
114pub mod sizes {
115 use core::mem::size_of;
116
117 /// Size of Option<T> for non-nullable T
118 pub const OPTION_OVERHEAD: usize = 1;
119
120 /// Pointer size (32-bit: 4 bytes, 64-bit: 8 bytes)
121 pub const PTR_SIZE: usize = size_of::<usize>();
122
123 /// Vec/String header size (ptr + len + cap)
124 pub const VEC_HEADER_SIZE: usize = 3 * PTR_SIZE;
125
126 /// Default Vec capacity on first allocation
127 pub const VEC_DEFAULT_CAPACITY: usize = 4;
128}
129
130/// Specialized array types with compile-time known sizes
131pub mod fixed {
132 /// Fixed-size array wrapper for stack allocation
133 ///
134 /// **Performance**: Zero heap allocation, inlined operations
135 /// **Use case**: When maximum size known at compile-time
136 #[repr(transparent)]
137 pub struct FixedArray<T, const N: usize>([T; N]);
138
139 impl<T, const N: usize> FixedArray<T, N> {
140 /// Create from array (zero-cost)
141 #[inline(always)]
142 pub const fn new(array: [T; N]) -> Self {
143 Self(array)
144 }
145
146 /// Get slice view
147 #[inline(always)]
148 pub const fn as_slice(&self) -> &[T] {
149 &self.0
150 }
151
152 /// Get mutable slice view
153 #[inline(always)]
154 pub fn as_mut_slice(&mut self) -> &mut [T] {
155 &mut self.0
156 }
157
158 /// Compile-time size
159 #[inline(always)]
160 pub const fn len(&self) -> usize {
161 N
162 }
163
164 /// Unwrap into inner array
165 #[inline(always)]
166 pub fn into_inner(self) -> [T; N] {
167 self.0
168 }
169 }
170
171 /// Small string optimization - stores up to 23 bytes inline
172 ///
173 /// **Memory**: 24 bytes total (same as String header)
174 /// **Benefit**: No heap allocation for short strings
175 #[repr(C)]
176 pub union SmallString {
177 /// Inline storage for strings <= 23 bytes
178 inline: core::mem::ManuallyDrop<InlineString>,
179 /// Heap pointer for strings > 23 bytes
180 heap: core::mem::ManuallyDrop<HeapString>,
181 }
182
183 #[repr(C)]
184 #[derive(Copy, Clone)]
185 struct InlineString {
186 data: [u8; 23],
187 len: u8, // MSB = 0 for inline
188 }
189
190 #[repr(C)]
191 #[derive(Copy, Clone)]
192 struct HeapString {
193 ptr: *mut u8,
194 cap: usize,
195 len: usize, // MSB = 1 for heap
196 }
197}
198
199/// Iterator utilities and extensions
200pub mod iter {
201 use super::*;
202
203 /// Trait for converting into iterator with size hint
204 pub trait IntoIteratorWithHint: IntoIterator {
205 /// Get size hint before consuming
206 fn size_hint_before(&self) -> (usize, Option<usize>);
207 }
208
209 impl<T> IntoIteratorWithHint for DynamicArray<T> {
210 #[inline]
211 fn size_hint_before(&self) -> (usize, Option<usize>) {
212 let len = self.len();
213 (len, Some(len))
214 }
215 }
216}
217
218/// Macro for convenient map initialization with capacity hint
219///
220/// **Optimization**: Pre-allocates exact capacity to avoid rehashing
221///
222/// # Examples
223/// ```
224/// # use avila_atom::map;
225/// let m = map! {
226/// "key1" => "value1",
227/// "key2" => "value2",
228/// };
229/// assert_eq!(m.get("key1"), Some(&"value1"));
230/// ```
231#[macro_export]
232macro_rules! map {
233 ($($key:expr => $value:expr),* $(,)?) => {{
234 // Count entries at compile-time
235 const COUNT: usize = {
236 let mut count = 0;
237 $( let _ = $key; count += 1; )*
238 count
239 };
240
241 let mut m = $crate::AssociativeArray::with_capacity(COUNT);
242 $(
243 m.insert($key, $value);
244 )*
245 m
246 }};
247}
248
249/// Macro for convenient vector initialization
250///
251/// **Note**: Delegates to stdlib `vec![]` which uses optimized inline assembly
252///
253/// # Examples
254/// ```
255/// # use avila_atom::list;
256/// let v = list![1, 2, 3, 4, 5];
257/// assert_eq!(v.len(), 5);
258/// ```
259#[macro_export]
260macro_rules! list {
261 ($($item:expr),* $(,)?) => {{
262 vec![$($item),*]
263 }};
264}
265
266/// Macro for creating fixed-size arrays with type inference
267///
268/// **Performance**: Stack-allocated, zero heap operations
269///
270/// # Examples
271/// ```
272/// # use avila_atom::array;
273/// let arr = array![1, 2, 3, 4];
274/// assert_eq!(arr.len(), 4);
275/// ```
276#[macro_export]
277macro_rules! array {
278 ($($item:expr),* $(,)?) => {{
279 $crate::fixed::FixedArray::new([$($item),*])
280 }};
281}
282
283/// Macro for compile-time size assertions
284///
285/// **Purpose**: Verify structure sizes match expectations for ABI compatibility
286///
287/// # Examples
288/// ```
289/// # use avila_atom::static_assert_size;
290/// static_assert_size!(Option<&str>, 16); // Two pointers on 64-bit
291/// ```
292#[macro_export]
293macro_rules! static_assert_size {
294 ($type:ty, $expected:expr) => {
295 const _: [(); $expected] = [(); ::core::mem::size_of::<$type>()];
296 };
297}
298
299#[cfg(test)]
300mod tests {
301 use super::*;
302
303 #[test]
304 fn test_option() {
305 let some: Option<i32> = Option::Some(42);
306 let none: Option<i32> = Option::None;
307
308 assert!(some.is_some());
309 assert!(none.is_none());
310 }
311
312 #[test]
313 fn test_result() {
314 let ok: Result<i32, &str> = Result::Ok(42);
315 let err: Result<i32, &str> = Result::Err("error");
316
317 assert!(ok.is_ok());
318 assert!(err.is_err());
319 }
320
321 #[test]
322 fn test_dynamic_array() {
323 let mut v: DynamicArray<i32> = DynamicArray::new();
324 assert_eq!(v.capacity(), 0); // Zero allocation initially
325
326 v.push(1);
327 v.push(2);
328 v.push(3);
329
330 assert_eq!(v.len(), 3);
331 assert!(v.capacity() >= 3); // Capacity grows as needed
332 }
333
334 #[test]
335 fn test_dynamic_array_with_capacity() {
336 let v: DynamicArray<i32> = DynamicArray::with_capacity(100);
337 assert_eq!(v.capacity(), 100);
338 assert_eq!(v.len(), 0);
339 }
340
341 #[test]
342 #[cfg(feature = "std")]
343 fn test_associative_array() {
344 let m = map! {
345 "key1" => "value1",
346 "key2" => "value2",
347 };
348
349 assert_eq!(m.get("key1"), Some(&"value1"));
350 assert_eq!(m.get("key2"), Some(&"value2"));
351 assert_eq!(m.len(), 2);
352 }
353
354 #[test]
355 fn test_string_buffer() {
356 let s: StringBuffer = StringBuffer::from("Hello, Avila!");
357 assert_eq!(s.len(), 13);
358 assert!(s.is_ascii()); // Performance hint: ASCII-only allows SIMD
359 }
360
361 #[test]
362 fn test_string_buffer_utf8() {
363 let s: StringBuffer = StringBuffer::from("Ávila 🚀");
364 assert_eq!(s.chars().count(), 7); // Character count
365 assert_eq!(s.len(), 11); // Byte count (UTF-8 encoded)
366 }
367
368 #[test]
369 fn test_fixed_array() {
370 use crate::fixed::FixedArray;
371
372 let arr = FixedArray::new([1, 2, 3, 4, 5]);
373 assert_eq!(arr.len(), 5);
374 assert_eq!(arr.as_slice()[0], 1);
375 }
376
377 #[test]
378 fn test_option_size_optimization() {
379 use core::mem::size_of;
380
381 // Null pointer optimization: Option<&T> same size as *const T
382 assert_eq!(size_of::<Option<&i32>>(), size_of::<&i32>());
383 assert_eq!(size_of::<Option<Box<i32>>>(), size_of::<Box<i32>>());
384 }
385
386 #[test]
387 fn test_result_error_handling() {
388 fn divide(a: i32, b: i32) -> Result<i32, &'static str> {
389 if b == 0 {
390 Err("Division by zero")
391 } else {
392 Ok(a / b)
393 }
394 }
395
396 assert_eq!(divide(10, 2), Ok(5));
397 assert_eq!(divide(10, 0), Err("Division by zero"));
398 }
399
400 #[test]
401 fn test_iterator_performance() {
402 use crate::iter::IntoIteratorWithHint;
403
404 let v = list![1, 2, 3, 4, 5];
405 let (lower, upper) = v.size_hint_before();
406
407 assert_eq!(lower, 5);
408 assert_eq!(upper, Some(5));
409 }
410
411 #[test]
412 #[cfg(feature = "std")]
413 fn test_map_macro_capacity() {
414 let m = map! {
415 1 => "one",
416 2 => "two",
417 3 => "three",
418 };
419
420 // Map should be pre-allocated with exact capacity
421 assert!(m.capacity() >= 3);
422 }
423}