Skip to content

Commit a9fe8d8

Browse files
committed
Add some missing smol_str API pieces
1 parent b4f9fac commit a9fe8d8

4 files changed

Lines changed: 144 additions & 7 deletions

File tree

src/tools/rust-analyzer/lib/smol_str/src/borsh.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,8 @@ impl BorshDeserialize for SmolStr {
2929
}))
3030
} else {
3131
// u8::vec_from_reader always returns Some on success in current implementation
32-
let vec = u8::vec_from_reader(len, reader)?.ok_or_else(|| {
33-
Error::new(ErrorKind::Other, "u8::vec_from_reader unexpectedly returned None")
34-
})?;
32+
let vec = u8::vec_from_reader(len, reader)?
33+
.ok_or_else(|| Error::other("u8::vec_from_reader unexpectedly returned None"))?;
3534
Ok(SmolStr::from(String::from_utf8(vec).map_err(|err| {
3635
let msg = err.to_string();
3736
Error::new(ErrorKind::InvalidData, msg)

src/tools/rust-analyzer/lib/smol_str/src/lib.rs

Lines changed: 117 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,17 @@ use core::{
3434
pub struct SmolStr(Repr);
3535

3636
impl SmolStr {
37+
/// The maximum byte length of a string that can be stored inline
38+
/// without heap allocation.
39+
pub const INLINE_CAP: usize = INLINE_CAP;
40+
3741
/// Constructs an inline variant of `SmolStr`.
3842
///
3943
/// This never allocates.
4044
///
4145
/// # Panics
4246
///
43-
/// Panics if `text.len() > 23`.
47+
/// Panics if `text.len() > `[`SmolStr::INLINE_CAP`].
4448
#[inline]
4549
pub const fn new_inline(text: &str) -> SmolStr {
4650
assert!(text.len() <= INLINE_CAP); // avoids bounds checks in loop
@@ -241,6 +245,48 @@ impl PartialOrd for SmolStr {
241245
}
242246
}
243247

248+
impl PartialOrd<str> for SmolStr {
249+
fn partial_cmp(&self, other: &str) -> Option<Ordering> {
250+
Some(self.as_str().cmp(other))
251+
}
252+
}
253+
254+
impl<'a> PartialOrd<&'a str> for SmolStr {
255+
fn partial_cmp(&self, other: &&'a str) -> Option<Ordering> {
256+
Some(self.as_str().cmp(*other))
257+
}
258+
}
259+
260+
impl PartialOrd<SmolStr> for &str {
261+
fn partial_cmp(&self, other: &SmolStr) -> Option<Ordering> {
262+
Some((*self).cmp(other.as_str()))
263+
}
264+
}
265+
266+
impl PartialOrd<String> for SmolStr {
267+
fn partial_cmp(&self, other: &String) -> Option<Ordering> {
268+
Some(self.as_str().cmp(other.as_str()))
269+
}
270+
}
271+
272+
impl PartialOrd<SmolStr> for String {
273+
fn partial_cmp(&self, other: &SmolStr) -> Option<Ordering> {
274+
Some(self.as_str().cmp(other.as_str()))
275+
}
276+
}
277+
278+
impl<'a> PartialOrd<&'a String> for SmolStr {
279+
fn partial_cmp(&self, other: &&'a String) -> Option<Ordering> {
280+
Some(self.as_str().cmp(other.as_str()))
281+
}
282+
}
283+
284+
impl PartialOrd<SmolStr> for &String {
285+
fn partial_cmp(&self, other: &SmolStr) -> Option<Ordering> {
286+
Some(self.as_str().cmp(other.as_str()))
287+
}
288+
}
289+
244290
impl hash::Hash for SmolStr {
245291
fn hash<H: hash::Hasher>(&self, hasher: &mut H) {
246292
self.as_str().hash(hasher);
@@ -385,6 +431,20 @@ impl AsRef<std::path::Path> for SmolStr {
385431
}
386432
}
387433

434+
impl From<char> for SmolStr {
435+
#[inline]
436+
fn from(c: char) -> SmolStr {
437+
let mut buf = [0; INLINE_CAP];
438+
let len = c.len_utf8();
439+
c.encode_utf8(&mut buf);
440+
SmolStr(Repr::Inline {
441+
// SAFETY: A char is at most 4 bytes, which is always <= INLINE_CAP (23).
442+
len: unsafe { InlineSize::transmute_from_u8(len as u8) },
443+
buf,
444+
})
445+
}
446+
}
447+
388448
impl From<&str> for SmolStr {
389449
#[inline]
390450
fn from(s: &str) -> SmolStr {
@@ -888,10 +948,18 @@ macro_rules! format_smolstr {
888948
/// A builder that can be used to efficiently build a [`SmolStr`].
889949
///
890950
/// This won't allocate if the final string fits into the inline buffer.
891-
#[derive(Clone, Default, Debug, PartialEq, Eq)]
951+
#[derive(Clone, Default, Debug)]
892952
pub struct SmolStrBuilder(SmolStrBuilderRepr);
893953

894-
#[derive(Clone, Debug, PartialEq, Eq)]
954+
impl PartialEq for SmolStrBuilder {
955+
fn eq(&self, other: &Self) -> bool {
956+
self.as_str() == other.as_str()
957+
}
958+
}
959+
960+
impl Eq for SmolStrBuilder {}
961+
962+
#[derive(Clone, Debug)]
895963
enum SmolStrBuilderRepr {
896964
Inline { len: usize, buf: [u8; INLINE_CAP] },
897965
Heap(String),
@@ -911,6 +979,52 @@ impl SmolStrBuilder {
911979
Self(SmolStrBuilderRepr::Inline { buf: [0; INLINE_CAP], len: 0 })
912980
}
913981

982+
/// Creates a new empty [`SmolStrBuilder`] with at least the specified capacity.
983+
///
984+
/// If `capacity` is less than or equal to [`SmolStr::INLINE_CAP`], the builder
985+
/// will use inline storage and not allocate. Otherwise, it will pre-allocate a
986+
/// heap buffer of the requested capacity.
987+
#[must_use]
988+
pub fn with_capacity(capacity: usize) -> Self {
989+
if capacity <= INLINE_CAP {
990+
Self::new()
991+
} else {
992+
Self(SmolStrBuilderRepr::Heap(String::with_capacity(capacity)))
993+
}
994+
}
995+
996+
/// Returns the number of bytes accumulated in the builder so far.
997+
#[inline]
998+
pub fn len(&self) -> usize {
999+
match &self.0 {
1000+
SmolStrBuilderRepr::Inline { len, .. } => *len,
1001+
SmolStrBuilderRepr::Heap(heap) => heap.len(),
1002+
}
1003+
}
1004+
1005+
/// Returns `true` if the builder has a length of zero bytes.
1006+
#[inline]
1007+
pub fn is_empty(&self) -> bool {
1008+
match &self.0 {
1009+
SmolStrBuilderRepr::Inline { len, .. } => *len == 0,
1010+
SmolStrBuilderRepr::Heap(heap) => heap.is_empty(),
1011+
}
1012+
}
1013+
1014+
/// Returns a `&str` slice of the builder's current contents.
1015+
#[inline]
1016+
pub fn as_str(&self) -> &str {
1017+
match &self.0 {
1018+
SmolStrBuilderRepr::Inline { len, buf } => {
1019+
// SAFETY: `buf[..*len]` was built by prior `push`/`push_str` calls
1020+
// that only wrote valid UTF-8, and `*len <= INLINE_CAP` is maintained
1021+
// by the inline branch logic.
1022+
unsafe { core::str::from_utf8_unchecked(&buf[..*len]) }
1023+
}
1024+
SmolStrBuilderRepr::Heap(heap) => heap.as_str(),
1025+
}
1026+
}
1027+
9141028
/// Builds a [`SmolStr`] from `self`.
9151029
#[must_use]
9161030
pub fn finish(self) -> SmolStr {

src/tools/rust-analyzer/lib/smol_str/src/serde.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ where
1616
impl<'a> Visitor<'a> for SmolStrVisitor {
1717
type Value = SmolStr;
1818

19-
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
19+
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
2020
formatter.write_str("a string")
2121
}
2222

src/tools/rust-analyzer/lib/smol_str/tests/test.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use smol_str::{SmolStr, SmolStrBuilder};
1010
#[cfg(target_pointer_width = "64")]
1111
fn smol_str_is_smol() {
1212
assert_eq!(::std::mem::size_of::<SmolStr>(), ::std::mem::size_of::<String>(),);
13+
assert_eq!(::std::mem::size_of::<Option<SmolStr>>(), ::std::mem::size_of::<SmolStr>(),);
1314
}
1415

1516
#[test]
@@ -332,6 +333,29 @@ fn test_builder_push() {
332333
assert_eq!("a".repeat(24), s);
333334
}
334335

336+
#[test]
337+
fn test_from_char() {
338+
// ASCII char
339+
let s: SmolStr = 'a'.into();
340+
assert_eq!(s, "a");
341+
assert!(!s.is_heap_allocated());
342+
343+
// Multi-byte char (2 bytes)
344+
let s: SmolStr = SmolStr::from('ñ');
345+
assert_eq!(s, "ñ");
346+
assert!(!s.is_heap_allocated());
347+
348+
// 3-byte char
349+
let s: SmolStr = '€'.into();
350+
assert_eq!(s, "€");
351+
assert!(!s.is_heap_allocated());
352+
353+
// 4-byte char (emoji)
354+
let s: SmolStr = '🦀'.into();
355+
assert_eq!(s, "🦀");
356+
assert!(!s.is_heap_allocated());
357+
}
358+
335359
#[cfg(test)]
336360
mod test_str_ext {
337361
use smol_str::StrExt;

0 commit comments

Comments
 (0)