pub union SmallBytes {
/* private fields */
}Expand description
A 24-byte owned byte string with inline small-string optimization.
Strings of up to 23 bytes live entirely inside the value (no allocation, no pointer chase); larger strings spill to a heap buffer. The discriminator is a single byte at offset 23 (the tag, which doubles as the inline length 0..=22 OR equals 0xFF when the heap variant is active).
See the crate root for layout details.
§Examples
Short values live inline; longer ones move to the heap. The API does not
change, but heap_bytes reports which happened, which is what the
keyspace’s memory accounting reads.
use kevy_bytes::SmallBytes;
let short = SmallBytes::from_slice(b"hello");
assert_eq!(short.as_slice(), b"hello");
assert_eq!(short.len(), 5);
assert_eq!(short.heap_bytes(), 0, "a short value allocates nothing");
let long = SmallBytes::from_slice(&[b'x'; 100]);
assert_eq!(long.len(), 100);
assert!(long.heap_bytes() >= 100, "a long value is on the heap");use kevy_bytes::SmallBytes;
assert!(SmallBytes::from_slice(b"").is_empty());Implementations§
Source§impl SmallBytes
impl SmallBytes
Sourcepub const fn new() -> Self
pub const fn new() -> Self
Empty inline SmallBytes (zero allocation).
§Examples
const, so it can seed a static or an array without a run-time
initialiser:
use kevy_bytes::SmallBytes;
static EMPTY: SmallBytes = SmallBytes::new();
assert!(EMPTY.is_empty());
assert_eq!(EMPTY.heap_bytes(), 0);Sourcepub fn from_slice(bytes: &[u8]) -> Self
pub fn from_slice(bytes: &[u8]) -> Self
Construct from a byte slice — inline if bytes.len() <= 23, else heap.
§Examples
Twenty-three is the boundary, and it is exact:
use kevy_bytes::SmallBytes;
assert_eq!(SmallBytes::from_slice(&[b'x'; 23]).heap_bytes(), 0);
assert_eq!(SmallBytes::from_slice(&[b'x'; 24]).heap_bytes(), 24);Sourcepub fn from_vec(vec: Vec<u8>) -> Self
pub fn from_vec(vec: Vec<u8>) -> Self
Take ownership of a Vec<u8> — inline if vec.len() <= 23, else reuse
the vec’s allocation (no copy on the heap path).
§Examples
The heap path keeps the vec’s own buffer, so a value that arrived as
a Vec is stored without a second copy:
use kevy_bytes::SmallBytes;
let v = vec![b'z'; 64];
let addr = v.as_ptr();
let b = SmallBytes::from_vec(v);
assert_eq!(b.as_slice().as_ptr(), addr, "same allocation, not a copy");A short vec goes inline instead, and its allocation is released:
use kevy_bytes::SmallBytes;
assert_eq!(SmallBytes::from_vec(vec![b'a'; 4]).heap_bytes(), 0);Sourcepub fn len(&self) -> usize
pub fn len(&self) -> usize
Number of bytes stored.
§Examples
The same answer either side of the inline boundary — which is the point of the type: where the bytes live is not the caller’s problem.
use kevy_bytes::SmallBytes;
assert_eq!(SmallBytes::from_slice(&[0u8; 22]).len(), 22);
assert_eq!(SmallBytes::from_slice(&[0u8; 23]).len(), 23);Sourcepub fn is_empty(&self) -> bool
pub fn is_empty(&self) -> bool
Whether len() == 0.
§Examples
use kevy_bytes::SmallBytes;
assert!(SmallBytes::from_slice(b"").is_empty());
assert!(!SmallBytes::from_slice(b"\0").is_empty(), "a NUL byte is a byte");Sourcepub fn heap_bytes(&self) -> usize
pub fn heap_bytes(&self) -> usize
Bytes this value holds on the heap (0 when inline). Lets memory-accounting
callers (e.g. maxmemory enforcement) charge only the off-stack footprint
without re-deriving the inline-length threshold.
This is the allocation, not the live length, and those differ:
Self::from_vec adopts its argument’s buffer as it stands, so a
Vec grown by extend_from_slice arrives with the doubling
ladder’s slack still on it. Reporting len charged 360 bytes for
a 640-byte allocation on the eleventh APPEND to one key — and
maxmemory is what this number feeds, so the server could sit at
1.8x its bound without evicting.
§Examples
This is what maxmemory charges, so an inline value must cost zero
— it is already inside the entry the keyspace has counted:
use kevy_bytes::SmallBytes;
assert_eq!(SmallBytes::from_slice(b"user:1").heap_bytes(), 0);
// `from_slice` allocates exactly, so here the two agree.
assert_eq!(SmallBytes::from_slice(&[b'x'; 1000]).heap_bytes(), 1000);
// A buffer with slack does not, and the slack is real memory.
let mut v = Vec::with_capacity(4096);
v.extend_from_slice(&[b'x'; 1000]);
assert_eq!(SmallBytes::from_vec(v).heap_bytes(), 4096);Sourcepub fn heap_bytes_for(bytes: &[u8]) -> usize
pub fn heap_bytes_for(bytes: &[u8]) -> usize
Heap bytes a SmallBytes built from bytes would own.
The same rule as Self::heap_bytes, answerable without building
the value — for accounting a key by its slice before it is stored.
Exists so the inline threshold is not copied out of this crate:
it was, as the literal 22, and any change to the boundary would
have silently mis-charged every key with nothing failing.
§Examples
use kevy_bytes::SmallBytes;
assert_eq!(SmallBytes::heap_bytes_for(b"user:1"), 0);
assert_eq!(SmallBytes::heap_bytes_for(&[b'x'; 1000]), 1000);Sourcepub fn as_slice(&self) -> &[u8] ⓘ
pub fn as_slice(&self) -> &[u8] ⓘ
Borrow the bytes (no allocation; same for inline and heap variants).
§Examples
use kevy_bytes::SmallBytes;
let b = SmallBytes::from_slice(b"GET");
assert_eq!(b.as_slice(), b"GET");
assert_eq!(SmallBytes::new().as_slice(), b"");Sourcepub fn to_vec(&self) -> Vec<u8> ⓘ
pub fn to_vec(&self) -> Vec<u8> ⓘ
Copy into a fresh Vec<u8> (clone semantics).
§Examples
use kevy_bytes::SmallBytes;
let b = SmallBytes::from_slice(b"copy me");
assert_eq!(b.to_vec(), b"copy me");
assert_eq!(b.as_slice(), b"copy me", "the original still holds them");Sourcepub fn into_vec(self) -> Vec<u8> ⓘ
pub fn into_vec(self) -> Vec<u8> ⓘ
Consume self and return an owned Vec<u8>. The heap path reuses the
existing allocation; the inline path copies into a new vec.
§Examples
A heap value hands its buffer straight back, so a round trip through
SmallBytes costs no allocation at either end:
use kevy_bytes::SmallBytes;
let v = vec![b'q'; 128];
let addr = v.as_ptr();
assert_eq!(SmallBytes::from_vec(v).into_vec().as_ptr(), addr);use kevy_bytes::SmallBytes;
assert_eq!(SmallBytes::from_slice(b"short").into_vec(), b"short");Trait Implementations§
Source§impl AsRef<[u8]> for SmallBytes
impl AsRef<[u8]> for SmallBytes
Source§impl Borrow<[u8]> for SmallBytes
impl Borrow<[u8]> for SmallBytes
Source§impl Clone for SmallBytes
impl Clone for SmallBytes
Source§fn clone(&self) -> Self
fn clone(&self) -> Self
Specialised clone that bypasses as_slice → from_slice → alloc_heap’s
two layered length checks. Inline variant is a bitwise union copy (no
branch through the slice path); heap variant goes straight to a single
alloc + memcpy keyed on the already-known heap length.
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for SmallBytes
impl Debug for SmallBytes
Source§impl Default for SmallBytes
impl Default for SmallBytes
Source§impl Drop for SmallBytes
impl Drop for SmallBytes
impl Eq for SmallBytes
Source§impl From<&[u8]> for SmallBytes
impl From<&[u8]> for SmallBytes
Source§impl Hash for SmallBytes
impl Hash for SmallBytes
Source§impl KevyHash for SmallBytes
KevyHash agrees with the byte-slice impl, so a KevyMap<SmallBytes, V>
can be queried with &[u8] (via Borrow<[u8]>) and the hash matches.
impl KevyHash for SmallBytes
KevyHash agrees with the byte-slice impl, so a KevyMap<SmallBytes, V>
can be queried with &[u8] (via Borrow<[u8]>) and the hash matches.
Source§impl Ord for SmallBytes
impl Ord for SmallBytes
1.21.0 (const: unstable) · Source§fn max(self, other: Self) -> Selfwhere
Self: Sized,
fn max(self, other: Self) -> Selfwhere
Self: Sized,
1.21.0 (const: unstable) · Source§fn min(self, other: Self) -> Selfwhere
Self: Sized,
fn min(self, other: Self) -> Selfwhere
Self: Sized,
Source§impl PartialEq for SmallBytes
impl PartialEq for SmallBytes
Source§fn eq(&self, other: &Self) -> bool
fn eq(&self, other: &Self) -> bool
Specialised over the slice form (as_slice == as_slice) by branching
on variant once and reading the relevant length / pointer pair
directly. Same-variant cases (inline/inline + heap/heap, which are the
only ones produced by a single allocator) skip a redundant as_slice
dispatch on each side; the mixed case falls back to the slice form.