oxigraph/storage/
small_string.rs

1use std::borrow::Borrow;
2use std::cmp::Ordering;
3use std::hash::{Hash, Hasher};
4use std::ops::Deref;
5use std::str::{FromStr, Utf8Error};
6use std::{fmt, str};
7
8/// A small inline string
9#[derive(Clone, Copy, Default)]
10pub struct SmallString {
11    inner: [u8; 16],
12}
13
14impl SmallString {
15    #[inline]
16    pub const fn new() -> Self {
17        Self { inner: [0; 16] }
18    }
19
20    #[inline]
21    pub fn from_utf8(bytes: &[u8]) -> Result<Self, BadSmallStringError> {
22        Self::from_str(str::from_utf8(bytes).map_err(BadSmallStringError::BadUtf8)?)
23    }
24
25    #[inline]
26    pub fn from_be_bytes(bytes: [u8; 16]) -> Result<Self, BadSmallStringError> {
27        // We check that it is valid UTF-8
28        str::from_utf8(&bytes.as_ref()[..bytes[15].into()])
29            .map_err(BadSmallStringError::BadUtf8)?;
30        Ok(Self { inner: bytes })
31    }
32
33    #[inline]
34    pub fn len(&self) -> usize {
35        self.inner[15].into()
36    }
37
38    #[inline]
39    pub fn is_empty(&self) -> bool {
40        self.len() == 0
41    }
42
43    #[inline]
44    #[allow(unsafe_code)]
45    pub fn as_str(&self) -> &str {
46        // SAFETY: safe because we ensured it in constructors
47        unsafe { str::from_utf8_unchecked(self.as_bytes()) }
48    }
49
50    #[inline]
51    pub fn as_bytes(&self) -> &[u8] {
52        &self.inner[..self.len()]
53    }
54
55    #[inline]
56    pub fn to_be_bytes(self) -> [u8; 16] {
57        self.inner
58    }
59}
60
61impl Deref for SmallString {
62    type Target = str;
63
64    #[inline]
65    fn deref(&self) -> &Self::Target {
66        self.as_str()
67    }
68}
69
70impl AsRef<str> for SmallString {
71    #[inline]
72    fn as_ref(&self) -> &str {
73        self.as_str()
74    }
75}
76
77impl Borrow<str> for SmallString {
78    #[inline]
79    fn borrow(&self) -> &str {
80        self.as_str()
81    }
82}
83
84impl fmt::Debug for SmallString {
85    #[inline]
86    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
87        self.as_str().fmt(f)
88    }
89}
90
91impl fmt::Display for SmallString {
92    #[inline]
93    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
94        self.as_str().fmt(f)
95    }
96}
97
98impl PartialEq for SmallString {
99    #[inline]
100    fn eq(&self, other: &Self) -> bool {
101        self.as_str() == other.as_str()
102    }
103}
104
105impl Eq for SmallString {}
106
107impl PartialOrd for SmallString {
108    #[inline]
109    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
110        Some(self.cmp(other))
111    }
112}
113
114impl Ord for SmallString {
115    #[inline]
116    fn cmp(&self, other: &Self) -> Ordering {
117        self.as_str().cmp(other.as_str())
118    }
119}
120
121impl Hash for SmallString {
122    #[inline]
123    fn hash<H: Hasher>(&self, state: &mut H) {
124        self.as_str().hash(state)
125    }
126}
127
128impl From<SmallString> for String {
129    #[inline]
130    fn from(value: SmallString) -> Self {
131        value.as_str().into()
132    }
133}
134
135impl<'a> From<&'a SmallString> for &'a str {
136    #[inline]
137    fn from(value: &'a SmallString) -> Self {
138        value.as_str()
139    }
140}
141
142impl FromStr for SmallString {
143    type Err = BadSmallStringError;
144
145    #[inline]
146    fn from_str(value: &str) -> Result<Self, Self::Err> {
147        if value.len() <= 15 {
148            let mut inner = [0; 16];
149            inner[..value.len()].copy_from_slice(value.as_bytes());
150            inner[15] = value
151                .len()
152                .try_into()
153                .map_err(|_| Self::Err::TooLong(value.len()))?;
154            Ok(Self { inner })
155        } else {
156            Err(Self::Err::TooLong(value.len()))
157        }
158    }
159}
160
161impl<'a> TryFrom<&'a str> for SmallString {
162    type Error = BadSmallStringError;
163
164    #[inline]
165    fn try_from(value: &'a str) -> Result<Self, Self::Error> {
166        Self::from_str(value)
167    }
168}
169
170#[derive(Debug, Clone, Copy, thiserror::Error)]
171pub enum BadSmallStringError {
172    #[error("small strings could only contain at most 15 characters, found {0}")]
173    TooLong(usize),
174    #[error(transparent)]
175    BadUtf8(#[from] Utf8Error),
176}