Skip to main content

odoo_lsp/
str.rs

1use core::mem::size_of;
2use std::borrow::Borrow;
3use std::fmt::Display;
4use std::ops::Deref;
5use std::path::Path;
6use std::sync::Arc;
7
8use const_format::assertcp_eq;
9use num_enum::TryFromPrimitive;
10
11/// Immutable, [`String`]-sized clone-friendly string.
12#[derive(Clone)]
13pub struct ImStr(Repr);
14
15const INLINE_BYTES: usize = if cfg!(target_pointer_width = "64") { 23 } else { 11 };
16#[derive(Clone)]
17pub(crate) enum Repr {
18	Arc(Arc<str>),
19	Inline(u23, [u8; INLINE_BYTES]),
20}
21
22#[allow(non_camel_case_types)]
23#[rustfmt::skip]
24#[derive(TryFromPrimitive, Clone, Copy)]
25#[repr(u8)]
26pub(crate) enum u23 {
27	_0, _1, _2, _3, _4, _5, _6, _7, _8, _9,
28	_10, _11, _12, _13, _14, _15, _16, _17, _18, _19,
29	_20, _21, _22, _23, 
30}
31
32impl Deref for ImStr {
33	type Target = str;
34	#[inline]
35	fn deref(&self) -> &Self::Target {
36		match &self.0 {
37			Repr::Arc(inner) => inner,
38			// Repr::Static(inner) => inner,
39			Repr::Inline(len, bytes) => {
40				let slice = &bytes[..(*len as usize)];
41				unsafe { std::str::from_utf8_unchecked(slice) }
42			}
43		}
44	}
45}
46
47impl Borrow<[u8]> for ImStr {
48	fn borrow(&self) -> &[u8] {
49		self.as_str().as_bytes()
50	}
51}
52
53impl From<&str> for ImStr {
54	fn from(value: &str) -> Self {
55		let src = value.as_bytes();
56		if src.len() <= INLINE_BYTES {
57			let mut bytes = [0u8; INLINE_BYTES];
58			let slice = &mut bytes[..src.len()];
59			slice.copy_from_slice(src);
60			Self(Repr::Inline(u23::try_from(src.len() as u8).unwrap(), bytes))
61		} else {
62			Self(Repr::Arc(Arc::from(value)))
63		}
64	}
65}
66
67impl From<String> for ImStr {
68	#[inline]
69	fn from(value: String) -> Self {
70		value.as_str().into()
71	}
72}
73
74impl AsRef<str> for ImStr {
75	#[inline]
76	fn as_ref(&self) -> &str {
77		self.deref()
78	}
79}
80
81impl Display for ImStr {
82	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
83		Display::fmt(self.deref(), f)
84	}
85}
86
87impl std::fmt::Debug for ImStr {
88	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
89		std::fmt::Debug::fmt(self.deref(), f)
90	}
91}
92
93impl<T> PartialEq<T> for ImStr
94where
95	T: AsRef<str>,
96{
97	#[inline]
98	fn eq(&self, other: &T) -> bool {
99		self.deref() == other.as_ref()
100	}
101}
102
103impl<T> PartialOrd<T> for ImStr
104where
105	T: AsRef<str>,
106{
107	#[inline]
108	fn partial_cmp(&self, other: &T) -> Option<std::cmp::Ordering> {
109		self.deref().partial_cmp(other.as_ref())
110	}
111}
112
113impl PartialEq<ImStr> for str {
114	fn eq(&self, other: &ImStr) -> bool {
115		self == other.deref()
116	}
117}
118
119impl Eq for ImStr {}
120impl Ord for ImStr {
121	#[inline]
122	fn cmp(&self, other: &Self) -> std::cmp::Ordering {
123		self.deref().cmp(other.deref())
124	}
125}
126
127impl std::hash::Hash for ImStr {
128	#[inline]
129	fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
130		self.as_bytes().hash(state)
131	}
132}
133
134impl AsRef<Path> for ImStr {
135	fn as_ref(&self) -> &Path {
136		Path::new(self.as_str())
137	}
138}
139
140impl ImStr {
141	#[inline]
142	pub fn as_str(&self) -> &str {
143		self.deref()
144	}
145}
146
147const _: () = {
148	assertcp_eq!(
149		size_of::<ImStr>(),
150		size_of::<String>(),
151		"ImStr must be as large as String"
152	);
153	assertcp_eq!(
154		size_of::<Option<ImStr>>(),
155		size_of::<ImStr>(),
156		"An optional ImStr must be as large as ImStr"
157	);
158};