Experimental InliningString::empty, rename experimental ShString23
This commit is contained in:
1 parent
b10507ae24
commit
821c75729d
1 file changed
+68
-27
+68
-27
@@ -10,7 +10,7 @@ use std::{
|
|||||||
str,
|
str,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub type ShString23 = InliningString<23>;
|
pub type InliningString23 = InliningString<23>;
|
||||||
|
|
||||||
/// An experimental alternative to `libshire::strings::ShString`, which is able to store one extra
|
/// An experimental alternative to `libshire::strings::ShString`, which is able to store one extra
|
||||||
/// byte of string data on the stack in the same amount of space.
|
/// byte of string data on the stack in the same amount of space.
|
||||||
@@ -61,11 +61,26 @@ impl<const N: usize> InliningString<N> {
|
|||||||
match u8::try_from(src.len()) {
|
match u8::try_from(src.len()) {
|
||||||
Ok(len) if len <= Self::MAX_LEN => {
|
Ok(len) if len <= Self::MAX_LEN => {
|
||||||
unsafe {
|
unsafe {
|
||||||
|
// SAFETY:
|
||||||
|
// `MaybeUninit::uninit()` is a valid value for `[MaybeUninit<u8>; N]`, since
|
||||||
|
// each element of the array is allowed to be uninitialised.
|
||||||
let mut buf = MaybeUninit::<[MaybeUninit<u8>; N]>::uninit()
|
let mut buf = MaybeUninit::<[MaybeUninit<u8>; N]>::uninit()
|
||||||
.assume_init();
|
.assume_init();
|
||||||
|
|
||||||
|
// Cast the byte slice to a `MaybeUninit<u8>` pointer. This is valid because
|
||||||
|
// `u8` has the same memory layout as `MaybeUninit<u8>`.
|
||||||
let src_ptr = src.as_ptr() as *const MaybeUninit<u8>;
|
let src_ptr = src.as_ptr() as *const MaybeUninit<u8>;
|
||||||
|
|
||||||
|
// Copy the string data provided by the caller into the buffer.
|
||||||
|
// SAFETY:
|
||||||
|
// The source is valid because the source and length are both taken from a
|
||||||
|
// valid `&[u8]`. We have already checked in the match statement that there is
|
||||||
|
// enough space in the buffer to fit the string data (i.e. `len` is less than
|
||||||
|
// or equal to `MAX_LEN`, which is equal to `N`), so the destination is valid.
|
||||||
|
// The source and destination are trivially properly aligned because the
|
||||||
|
// alignment of `MaybeUninit<u8>` is 1. The source and destination do not
|
||||||
|
// overlap; the destination buffer is a new variable completely separate from
|
||||||
|
// the source data.
|
||||||
ptr::copy_nonoverlapping(src_ptr, buf.as_mut_ptr(), usize::from(len));
|
ptr::copy_nonoverlapping(src_ptr, buf.as_mut_ptr(), usize::from(len));
|
||||||
|
|
||||||
// SAFETY:
|
// SAFETY:
|
||||||
@@ -80,9 +95,27 @@ impl<const N: usize> InliningString<N> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
pub fn empty() -> Self {
|
||||||
|
unsafe {
|
||||||
|
// SAFETY:
|
||||||
|
// `MaybeUninit::uninit()` is a valid value for `[MaybeUninit<u8>; N]`, since each
|
||||||
|
// element of the array is allowed to be uninitialised.
|
||||||
|
let buf = MaybeUninit::<[MaybeUninit<u8>; N]>::uninit()
|
||||||
|
.assume_init();
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
|
// `len` is 0, so the contract that the first `len` bytes of `buf` are initialised and
|
||||||
|
// valid UTF-8 is trivially upheld.
|
||||||
|
Self::stack_from_raw_parts(buf, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// # Safety
|
/// # Safety
|
||||||
/// The first `len` bytes of `buf` must be valid UTF-8. `len` must be less than or equal to
|
/// The first `len` bytes of `buf` must be initialised and valid UTF-8. `len` must be less than
|
||||||
/// `Self::MAX_LEN` (which is equal to `N`).
|
/// or equal to `Self::MAX_LEN` (which is equal to `N`).
|
||||||
|
#[inline]
|
||||||
unsafe fn stack_from_raw_parts(buf: [MaybeUninit<u8>; N], len: u8) -> Self {
|
unsafe fn stack_from_raw_parts(buf: [MaybeUninit<u8>; N], len: u8) -> Self {
|
||||||
// SAFETY:
|
// SAFETY:
|
||||||
// The caller is responsible for ensuring that `len` is less than or equal to
|
// The caller is responsible for ensuring that `len` is less than or equal to
|
||||||
@@ -97,6 +130,7 @@ impl<const N: usize> InliningString<N> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn new_heap<S>(s: S) -> Self
|
fn new_heap<S>(s: S) -> Self
|
||||||
where
|
where
|
||||||
Box<str>: From<S>,
|
Box<str>: From<S>,
|
||||||
@@ -378,13 +412,20 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_align() {
|
fn test_align() {
|
||||||
use std::mem::align_of;
|
use std::mem::align_of;
|
||||||
assert_eq!(align_of::<InliningString<23>>(), align_of::<Box<str>>());
|
assert_eq!(align_of::<InliningString23>(), align_of::<Box<str>>());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_niche() {
|
fn test_niche() {
|
||||||
use std::mem::size_of;
|
use std::mem::size_of;
|
||||||
assert_eq!(size_of::<InliningString<23>>(), size_of::<Option<InliningString<23>>>());
|
assert_eq!(size_of::<InliningString23>(), size_of::<Option<InliningString23>>());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_empty() {
|
||||||
|
assert_eq!(InliningString23::empty().as_str(), "");
|
||||||
|
assert_eq!(InliningString23::empty().len(), 0);
|
||||||
|
assert!(!InliningString23::empty().heap_allocated());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -404,20 +445,20 @@ mod tests {
|
|||||||
let borrowed = Cow::Borrowed(s);
|
let borrowed = Cow::Borrowed(s);
|
||||||
let owned = Cow::<'static, str>::Owned(buf.clone());
|
let owned = Cow::<'static, str>::Owned(buf.clone());
|
||||||
|
|
||||||
assert_eq!(ShString23::new(s).as_str(), s);
|
assert_eq!(InliningString23::new(s).as_str(), s);
|
||||||
assert_eq!(ShString23::new(buf).as_str(), s);
|
assert_eq!(InliningString23::new(buf).as_str(), s);
|
||||||
assert_eq!(ShString23::new(borrowed).as_str(), s);
|
assert_eq!(InliningString23::new(borrowed).as_str(), s);
|
||||||
assert_eq!(ShString23::new(owned).as_str(), s);
|
assert_eq!(InliningString23::new(owned).as_str(), s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_as_str_mut() {
|
fn test_as_str_mut() {
|
||||||
let mut s1 = ShString23::new("hello");
|
let mut s1 = InliningString23::new("hello");
|
||||||
s1.as_str_mut().make_ascii_uppercase();
|
s1.as_str_mut().make_ascii_uppercase();
|
||||||
assert_eq!(s1.as_str(), "HELLO");
|
assert_eq!(s1.as_str(), "HELLO");
|
||||||
|
|
||||||
let mut s2 = ShString23::new("the quick brown fox jumps over the lazy dog");
|
let mut s2 = InliningString23::new("the quick brown fox jumps over the lazy dog");
|
||||||
s2.as_str_mut().make_ascii_uppercase();
|
s2.as_str_mut().make_ascii_uppercase();
|
||||||
assert_eq!(s2.as_str(), "THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG");
|
assert_eq!(s2.as_str(), "THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG");
|
||||||
}
|
}
|
||||||
@@ -435,32 +476,32 @@ mod tests {
|
|||||||
];
|
];
|
||||||
|
|
||||||
for s in test_strings {
|
for s in test_strings {
|
||||||
assert_eq!(ShString23::new(&*s).into_string(), s);
|
assert_eq!(InliningString23::new(&*s).into_string(), s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_len() {
|
fn test_len() {
|
||||||
assert_eq!(ShString23::new("").len(), 0);
|
assert_eq!(InliningString23::new("").len(), 0);
|
||||||
assert_eq!(ShString23::new("Hello").len(), 5);
|
assert_eq!(InliningString23::new("Hello").len(), 5);
|
||||||
assert_eq!(ShString23::new("Somethingfortheweekend").len(), 22);
|
assert_eq!(InliningString23::new("Somethingfortheweekend").len(), 22);
|
||||||
assert_eq!(ShString23::new("Dichlorodifluoromethane").len(), 23);
|
assert_eq!(InliningString23::new("Dichlorodifluoromethane").len(), 23);
|
||||||
assert_eq!(ShString23::new("Electrocardiographically").len(), 24);
|
assert_eq!(InliningString23::new("Electrocardiographically").len(), 24);
|
||||||
assert_eq!(ShString23::new("こんにちは").len(), 15);
|
assert_eq!(InliningString23::new("こんにちは").len(), 15);
|
||||||
assert_eq!(ShString23::new("❤️🧡💛💚💙💜").len(), 26);
|
assert_eq!(InliningString23::new("❤️🧡💛💚💙💜").len(), 26);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_heap_allocated() {
|
fn test_heap_allocated() {
|
||||||
assert!(!ShString23::new("").heap_allocated());
|
assert!(!InliningString23::new("").heap_allocated());
|
||||||
assert!(!ShString23::new("Hello").heap_allocated());
|
assert!(!InliningString23::new("Hello").heap_allocated());
|
||||||
assert!(!ShString23::new("Somethingfortheweekend").heap_allocated());
|
assert!(!InliningString23::new("Somethingfortheweekend").heap_allocated());
|
||||||
assert!(!ShString23::new("Dichlorodifluoromethane").heap_allocated());
|
assert!(!InliningString23::new("Dichlorodifluoromethane").heap_allocated());
|
||||||
assert!(!ShString23::new("こんにちは").heap_allocated());
|
assert!(!InliningString23::new("こんにちは").heap_allocated());
|
||||||
|
|
||||||
assert!(ShString23::new("Electrocardiographically").heap_allocated());
|
assert!(InliningString23::new("Electrocardiographically").heap_allocated());
|
||||||
assert!(ShString23::new("Squishedbuginsidethescreen").heap_allocated());
|
assert!(InliningString23::new("Squishedbuginsidethescreen").heap_allocated());
|
||||||
assert!(ShString23::new("❤️🧡💛💚💙💜").heap_allocated());
|
assert!(InliningString23::new("❤️🧡💛💚💙💜").heap_allocated());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
Reference in new issue
Block a user