1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
//! Saving space on owned heap-allocated slices and strings. //! //! If we have a [`String`] containing `hello`, it takes `5` bytes on the heap and whole 24 bytes //! on the stack (on 64bit platform). That's a lot of overhead. One can use `Box<str>` instead, //! that uses only 16 bytes on the stack. With this library, `6` bytes are on the heap and 8 on the //! stack (no, this is not *the* short string optimization ‒ that one stops being useful at very //! short strings). //! //! Also, this library works for other arrays/slices not just strings. //! //! The types work with null pointer optimisation (`Option<OwnedSlice<T>>` has the same size as //! [`OwnedSlice<T>`][OwnedSlice]) and empty slice/string doesn't allocate. //! //! The downside is, they can't change their length like [`String`] or [`Vec`]. Therefore, this is //! suited for storing large amounts of smallish strings. //! //! # How does it work //! //! The length is stored as a header on the heap, followed by the actual data. The length is //! variable length encoded ‒ short strings take only 1 byte header, longer ones take 2 bytes... //! There's a limit at how large the string can be (current limit is 2^38 characters). //! //! # Future plans //! //! The datastructures are parametrized by a [`Header`]. The future versions will have a limited //! [`Arc`][std::sync::Arc] or [`Rc`][std::rc::Rc] builtin functionality ‒ it'll be possible to //! share single string/slice between multiple owners. They'll still be sized one word on the //! stack. //! //! Also, there's a plan to be able to put multiple these variable length slices/strings inside a //! single allocationd behind a single pointer. Then it'll be possible to save even more on //! structures holding multiple shortish strings. But how the API will look like is still unknown. //! //! Support for integrating with other libraries (`serde`, `heapsize`) will be added behind feature //! flags. //! //! Support for allocating from an arena (eg. [`bumpalo`](https://crates.io/crates/bumpalo) to cut //! down on the allocator overhead might also come. //! //! # Features //! //! * The `std` feature (on by default) adds some little convenience details (eg. the [`TooLong`] //! implements [`std::error::Error`]). By opting out of this feature, the library needs only //! [`alloc`]. //! //! # Current quirks //! //! (Some of it may be lifted in future versions) //! //! The structures dereference to slice/`str`, but explicit dereferencing may be necessary at //! times. //! //! Sometimes it is needed to hint the type resolution with the right type (as in the example //! below). //! //! # Examples //! //! ```rust //! use squash::Str; //! //! // Takes 24 + 5 + allocator overhead //! let string = String::from("Hello"); //! // Takes 8 + 6 + allocator overhead //! let squashed_string: Str = Str::new(&string).unwrap(); //! //! assert_eq!(&string as &str, &squashed_string as &str); //! ``` //! //! # See also //! //! If you are trying to save some memory, you might also have a look at these: //! //! * [`smallvec`](https://crates.io/crates/smallvec) and //! [`smallstr`](https://crates.io/crates/smallstr) (alternatively also //! [`tinyvec`](https://crates.io/crates/tinyvec)). //! * [`arrayvec`](https://crates.io/crates/arrayvec) if you know an upper bound for the size. //! * [`bumpalo`](https://crates.io/crates/bumpalo) or another arena allocator. This doesn't make //! the actual size smaller, though. #![doc(test(attr(deny(warnings))))] #![warn(missing_docs)] #![cfg_attr(not(feature = "std"), no_std)] extern crate alloc; // TODO: ArcSwap support? Is it possible? // TODO: Serde support // TODO: HeapSize support // TODO: Bumpalo support // TODO: make_mut or similar APIs? // TODO: as_raw and similar? mod header; mod slice; mod wrapper; pub use header::boxed::BoxHeader; pub use header::{Header, TooLong}; pub use slice::OwnedSlice; pub use wrapper::str::Str;