oxigraph/storage/
small_string.rs1use 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#[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 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 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}