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.
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?
pub use BoxHeader;
pub use ;
pub use OwnedSlice;
pub use Str;