Skip to main content

squash/
lib.rs

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