stack_cstr/lib.rs
1//! # stack_cstr
2//!
3//! `stack_cstr` provides ergonomic and efficient ways to create
4//! [`CStr`](std::ffi::CStr) values for FFI interoperability.
5//!
6//! The crate offers both **stack-based** and **heap-based** strategies
7//! for storing C-compatible strings. Its goal is to minimize heap
8//! allocations for short-lived or short strings while providing
9//! automatic fallback to the heap when needed.
10//!
11//! ## Motivation
12//!
13//! When interacting with C APIs, strings must be passed as
14//! null-terminated `*const c_char`. The standard way in Rust
15//! is to use [`CString`], which always allocates on the heap.
16//!
17//! However, in performance-sensitive or embedded environments,
18//! frequent heap allocations are undesirable. `stack_cstr` allows
19//! you to create `CStr` objects backed by **fixed-size stack buffers**,
20//! avoiding heap allocations for common short strings.
21//!
22//! ## Core Components
23//!
24//! - [`CStrLike`](crate::CStrLike): A trait for types that can expose
25//!   a `*const c_char` and a `&CStr`.
26//! - [`CStrStack`](crate::CStrStack): A stack-allocated C string with
27//!   a user-defined capacity.
28//! - [`CStrHeap`](crate::CStrHeap): A heap-allocated C string wrapper
29//!   around [`CString`].
30//! - [`cstr!`](crate::cstr): A macro that automatically chooses between
31//!   stack or heap storage depending on the string length.
32//!
33//! ## Example: Using the `cstr!` Macro
34//!
35//! ```
36//! use std::ffi::CStr;
37//! use stack_cstr::{cstr, CStrLike};
38//!
39//! // Default stack sizes are [32, 128]
40//! let s = cstr!("Hello {}", 42);
41//! assert_eq!(s.as_cstr().to_str().unwrap(), "Hello 42");
42//!
43//! // Explicitly set candidate stack buffer sizes
44//! let s = cstr!([16, 64], "Pi = {:.2}", 3.14159);
45//! assert_eq!(s.as_cstr().to_str().unwrap(), "Pi = 3.14");
46//!
47//! unsafe {
48//!     // Pass to FFI as *const c_char
49//!     let ptr = s.as_ptr();
50//!     assert_eq!(CStr::from_ptr(ptr).to_str().unwrap(), "Pi = 3.14");
51//! }
52//! ```
53//!
54//! ## Example: Manual Use of `CStrStack`
55//!
56//! ```
57//! use stack_cstr::CStrStack;
58//!
59//! // Create a stack-allocated C string with capacity 32
60//! let s = CStrStack::<32>::new(format_args!("ABC {}!", 123)).unwrap();
61//! assert_eq!(s.as_cstr().to_str().unwrap(), "ABC 123!");
62//! ```
63//!
64//! ## Example: Manual Use of `CStrHeap`
65//!
66//! ```
67//! use std::ffi::CString;
68//! use stack_cstr::CStrHeap;
69//!
70//! let heap_str = CStrHeap::new(CString::new("Hello from heap").unwrap());
71//! assert_eq!(heap_str.as_cstr().to_str().unwrap(), "Hello from heap");
72//! ```
73//!
74//! ## Design Notes
75//!
76//! - `CStrStack` checks buffer boundaries at construction time and
77//!   guarantees proper null termination.
78//! - `cstr!` hides the complexity by trying multiple stack sizes and
79//!   falling back to heap when necessary.
80//! - Returned values from `cstr!` are `Box<dyn CStrLike>` for uniformity,
81//!   even when stack-backed. This introduces a single heap allocation
82//!   for type erasure, which is usually negligible compared to string allocation.
83//!
84//! ## Use Cases
85//!
86//! - Embedding short strings in FFI calls without heap allocation.
87//! - Performance-sensitive applications where allocation patterns matter.
88//! - Embedded systems where heap memory is constrained.
89//!
90//! ## Limitations
91//!
92//! - `CStrStack` requires a compile-time constant buffer size.
93//! - Even stack-backed strings returned from `cstr!` are wrapped in a `Box`
94//!   to allow dynamic dispatch.
95//!
96//! ## See Also
97//!
98//! - [`CString`](std::ffi::CString) for heap-allocated C strings
99//! - [`CStr`](std::ffi::CStr) for borrowed C strings
100//! - [`arrayvec`](https://docs.rs/arrayvec) used internally for formatting
101
102pub mod cstr_heap;
103pub mod cstr_like;
104pub mod cstr_stack;
105pub mod error;
106pub mod macros;
107
108pub use cstr_heap::CStrHeap;
109pub use cstr_like::CStrLike;
110pub use cstr_stack::CStrStack;
111pub use error::CStrError;