Unnamed repository; edit this file 'description' to name the repository.
Merge pull request rust-analyzer/smol_str#53 from Austaras/master
Closes https://github.com/rust-analyzer/smol_str/issues/45
Lukas Wirth 2023-02-05
parent 25d7fa7 · parent 5f367d7 · commit 3dc9563
-rw-r--r--lib/smol_str/Cargo.toml2
-rw-r--r--lib/smol_str/README.md4
-rw-r--r--lib/smol_str/src/lib.rs62
-rw-r--r--lib/smol_str/tests/test.rs12
4 files changed, 58 insertions, 22 deletions
diff --git a/lib/smol_str/Cargo.toml b/lib/smol_str/Cargo.toml
index b7bd8f7314..e46b14fa83 100644
--- a/lib/smol_str/Cargo.toml
+++ b/lib/smol_str/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "smol_str"
-version = "0.1.23"
+version = "0.1.24"
description = "small-string optimized string type with O(1) clone"
license = "MIT OR Apache-2.0"
repository = "https://github.com/rust-analyzer/smol_str"
diff --git a/lib/smol_str/README.md b/lib/smol_str/README.md
index 2e61b9ee42..0cc1910181 100644
--- a/lib/smol_str/README.md
+++ b/lib/smol_str/README.md
@@ -10,8 +10,8 @@ A `SmolStr` is a string type that has the following properties:
* `size_of::<SmolStr>() == size_of::<String>()`
* `Clone` is `O(1)`
* Strings are stack-allocated if they are:
- * Up to 22 bytes long
- * Longer than 22 bytes, but substrings of `WS` (see `src/lib.rs`). Such strings consist
+ * Up to 23 bytes long
+ * Longer than 23 bytes, but substrings of `WS` (see `src/lib.rs`). Such strings consist
solely of consecutive newlines, followed by consecutive spaces
* If a string does not satisfy the aforementioned conditions, it is heap-allocated
diff --git a/lib/smol_str/src/lib.rs b/lib/smol_str/src/lib.rs
index 8c92e51e1c..775c5d8f22 100644
--- a/lib/smol_str/src/lib.rs
+++ b/lib/smol_str/src/lib.rs
@@ -10,6 +10,7 @@ use core::{
cmp::{self, Ordering},
convert::Infallible,
fmt, hash, iter,
+ mem::transmute,
ops::Deref,
str::FromStr,
};
@@ -19,8 +20,8 @@ use core::{
/// * `size_of::<SmolStr>() == size_of::<String>()`
/// * `Clone` is `O(1)`
/// * Strings are stack-allocated if they are:
-/// * Up to 22 bytes long
-/// * Longer than 22 bytes, but substrings of `WS` (see below). Such strings consist
+/// * Up to 23 bytes long
+/// * Longer than 23 bytes, but substrings of `WS` (see below). Such strings consist
/// solely of consecutive newlines, followed by consecutive spaces
/// * If a string does not satisfy the aforementioned conditions, it is heap-allocated
///
@@ -51,16 +52,16 @@ impl SmolStr {
buf[$idx] = byte
});
}
- s!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21);
+ s!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22);
SmolStr(Repr::Inline {
- len: len as u8,
+ len: unsafe { transmute(len as u8) },
buf,
})
}
/// Constructs inline variant of `SmolStr`.
///
- /// Panics if `text.len() > 22`.
+ /// Panics if `text.len() > 23`.
#[inline]
pub const fn new_inline(text: &str) -> SmolStr {
let mut buf = [0; INLINE_CAP];
@@ -70,7 +71,7 @@ impl SmolStr {
i += 1
}
SmolStr(Repr::Inline {
- len: text.len() as u8,
+ len: unsafe { transmute(text.len() as u8) },
buf,
})
}
@@ -132,7 +133,7 @@ impl SmolStr {
len += size;
}
SmolStr(Repr::Inline {
- len: len as u8,
+ len: unsafe { transmute(len as u8) },
buf,
})
}
@@ -266,7 +267,7 @@ where
len += size;
}
SmolStr(Repr::Inline {
- len: len as u8,
+ len: unsafe { transmute(len as u8) },
buf,
})
}
@@ -327,17 +328,52 @@ impl<'a> arbitrary::Arbitrary<'a> for SmolStr {
}
}
-const INLINE_CAP: usize = 22;
+const INLINE_CAP: usize = 23;
const N_NEWLINES: usize = 32;
const N_SPACES: usize = 128;
const WS: &str =
"\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n ";
+#[derive(Clone, Copy, Debug)]
+#[repr(u8)]
+enum InlineSize {
+ _V0 = 0,
+ _V1,
+ _V2,
+ _V3,
+ _V4,
+ _V5,
+ _V6,
+ _V7,
+ _V8,
+ _V9,
+ _V10,
+ _V11,
+ _V12,
+ _V13,
+ _V14,
+ _V15,
+ _V16,
+ _V17,
+ _V18,
+ _V19,
+ _V20,
+ _V21,
+ _V22,
+ _V23,
+}
+
#[derive(Clone, Debug)]
enum Repr {
Heap(Arc<str>),
- Inline { len: u8, buf: [u8; INLINE_CAP] },
- Substring { newlines: usize, spaces: usize },
+ Inline {
+ len: InlineSize,
+ buf: [u8; INLINE_CAP],
+ },
+ Substring {
+ newlines: usize,
+ spaces: usize,
+ },
}
impl Repr {
@@ -353,7 +389,7 @@ impl Repr {
let mut buf = [0; INLINE_CAP];
buf[..len].copy_from_slice(text.as_bytes());
return Repr::Inline {
- len: len as u8,
+ len: unsafe { transmute(len as u8) },
buf,
};
}
@@ -390,7 +426,7 @@ impl Repr {
fn is_empty(&self) -> bool {
match self {
Repr::Heap(data) => data.is_empty(),
- Repr::Inline { len, .. } => *len == 0,
+ Repr::Inline { len, .. } => *len as u8 == 0,
// A substring isn't created for an empty string.
Repr::Substring { .. } => false,
}
diff --git a/lib/smol_str/tests/test.rs b/lib/smol_str/tests/test.rs
index 934cfa3c05..187b39f001 100644
--- a/lib/smol_str/tests/test.rs
+++ b/lib/smol_str/tests/test.rs
@@ -29,12 +29,12 @@ fn const_fn_ctor() {
const EMPTY: SmolStr = SmolStr::new_inline("");
const A: SmolStr = SmolStr::new_inline("A");
const HELLO: SmolStr = SmolStr::new_inline("HELLO");
- const LONG: SmolStr = SmolStr::new_inline("ABCDEFGHIZKLMNOPQRSTUV");
+ const LONG: SmolStr = SmolStr::new_inline("ABCDEFGHIZKLMNOPQRSTUVW");
assert_eq!(EMPTY, SmolStr::from(""));
assert_eq!(A, SmolStr::from("A"));
assert_eq!(HELLO, SmolStr::from("HELLO"));
- assert_eq!(LONG, SmolStr::from("ABCDEFGHIZKLMNOPQRSTUV"));
+ assert_eq!(LONG, SmolStr::from("ABCDEFGHIZKLMNOPQRSTUVW"));
}
#[allow(deprecated)]
@@ -43,19 +43,19 @@ fn old_const_fn_ctor() {
const EMPTY: SmolStr = SmolStr::new_inline_from_ascii(0, b"");
const A: SmolStr = SmolStr::new_inline_from_ascii(1, b"A");
const HELLO: SmolStr = SmolStr::new_inline_from_ascii(5, b"HELLO");
- const LONG: SmolStr = SmolStr::new_inline_from_ascii(22, b"ABCDEFGHIZKLMNOPQRSTUV");
+ const LONG: SmolStr = SmolStr::new_inline_from_ascii(23, b"ABCDEFGHIZKLMNOPQRSTUVW");
assert_eq!(EMPTY, SmolStr::from(""));
assert_eq!(A, SmolStr::from("A"));
assert_eq!(HELLO, SmolStr::from("HELLO"));
- assert_eq!(LONG, SmolStr::from("ABCDEFGHIZKLMNOPQRSTUV"));
+ assert_eq!(LONG, SmolStr::from("ABCDEFGHIZKLMNOPQRSTUVW"));
}
fn check_props(std_str: &str, smol: SmolStr) -> Result<(), proptest::test_runner::TestCaseError> {
prop_assert_eq!(smol.as_str(), std_str);
prop_assert_eq!(smol.len(), std_str.len());
prop_assert_eq!(smol.is_empty(), std_str.is_empty());
- if smol.len() <= 22 {
+ if smol.len() <= 23 {
prop_assert!(!smol.is_heap_allocated());
}
Ok(())
@@ -218,7 +218,7 @@ fn test_from_char_iterator() {
// String which has too many characters to even consider inlining: Chars::size_hint uses
// (`len` + 3) / 4. With `len` = 89, this results in 23, so `from_iter` will immediately
// heap allocate
- let raw: String = std::iter::repeat('a').take(22 * 4 + 1).collect();
+ let raw: String = std::iter::repeat('a').take(23 * 4 + 1).collect();
let s: SmolStr = raw.chars().collect();
assert_eq!(s.as_str(), raw);
assert!(s.is_heap_allocated());