Skip to main content

deser_core/
text.rs

1//! Compact borrowed or owned slices for atoms.
2//!
3//! Atoms hold text and bytes that either borrow from the data that is
4//! processed or are owned.  A [`Cow`] needs three words for this (the owned
5//! variant carries the capacity of a `String`), the types here need two: a
6//! pointer and a length whose highest bit marks owned data.  Owned data is a
7//! boxed slice without spare capacity.  This keeps [`Atom`](crate::Atom)
8//! small enough to carry text next to another value.
9//!
10//! The three bits below the owned bit are a small tag that other types can
11//! store in the slice (like the type of the value of an
12//! [`Implicit`](crate::Implicit)).  Slices ignore it.
13use alloc::borrow::{Borrow, Cow};
14use alloc::boxed::Box;
15use alloc::string::String;
16use core::cmp::Ordering;
17use core::fmt;
18use core::hash::{Hash, Hasher};
19use core::marker::PhantomData;
20use core::ops::Deref;
21use core::ptr::NonNull;
22
23/// The bit of the length that marks owned data.
24const OWNED: usize = 1 << (usize::BITS - 1);
25
26/// The shift of the tag stored in the length.
27const TAG_SHIFT: u32 = usize::BITS - 4;
28
29/// The bits of the length that hold the tag.
30const TAG: usize = 0b111 << TAG_SHIFT;
31
32/// The bits of the length that hold the length.
33///
34/// No slice is this large (this is a fraction of the address space of any
35/// 64-bit system), but the constructors check it as the length of owned
36/// data has to be exact to free it.
37const LEN: usize = !(OWNED | TAG);
38
39/// A byte slice that is borrowed for `'a` or owned (a `Box<[u8]>`).
40pub(crate) struct Slice<'a> {
41    ptr: NonNull<u8>,
42    // the length, the highest bit is set if the data is owned
43    len: usize,
44    _marker: PhantomData<&'a [u8]>,
45}
46
47// SAFETY: the slice is either a `&'a [u8]` or a `Box<[u8]>`, both of which
48// are `Send` and `Sync`.
49unsafe impl Send for Slice<'_> {}
50// SAFETY: see above
51unsafe impl Sync for Slice<'_> {}
52
53impl<'a> Slice<'a> {
54    #[inline]
55    pub(crate) const fn borrowed(data: &'a [u8]) -> Slice<'a> {
56        assert!(data.len() <= LEN, "slice too large");
57        Slice {
58            // SAFETY: the pointer of a slice is never null.  It's never
59            // written through as borrowed data is only read.
60            ptr: unsafe { NonNull::new_unchecked(data.as_ptr().cast_mut()) },
61            len: data.len(),
62            _marker: PhantomData,
63        }
64    }
65
66    #[inline]
67    pub(crate) fn owned(data: Box<[u8]>) -> Slice<'a> {
68        let len = data.len();
69        assert!(len <= LEN, "slice too large");
70        let ptr = Box::into_raw(data).cast::<u8>();
71        Slice {
72            // SAFETY: the pointer of a box is never null
73            ptr: unsafe { NonNull::new_unchecked(ptr) },
74            len: len | OWNED,
75            _marker: PhantomData,
76        }
77    }
78
79    #[inline]
80    pub(crate) fn len(&self) -> usize {
81        self.len & LEN
82    }
83
84    /// Returns the tag stored in the slice (`0` to `7`).
85    #[inline]
86    pub(crate) fn tag(&self) -> u8 {
87        ((self.len & TAG) >> TAG_SHIFT) as u8
88    }
89
90    /// Returns the slice with a tag (`0` to `7`).
91    #[inline]
92    pub(crate) fn with_tag(mut self, tag: u8) -> Slice<'a> {
93        debug_assert!(tag <= 7);
94        self.len = (self.len & !TAG) | ((tag as usize) << TAG_SHIFT) & TAG;
95        self
96    }
97
98    #[inline]
99    pub(crate) fn is_owned(&self) -> bool {
100        self.len & OWNED != 0
101    }
102
103    #[inline]
104    pub(crate) fn as_slice(&self) -> &[u8] {
105        // SAFETY: the pointer and length are the ones of a slice that is
106        // borrowed for `'a` or owned by this value.
107        unsafe { core::slice::from_raw_parts(self.ptr.as_ptr(), self.len()) }
108    }
109
110    /// Returns the data if it's borrowed for `'a`.
111    #[inline]
112    pub(crate) fn borrowed_slice(&self) -> Option<&'a [u8]> {
113        if self.is_owned() {
114            None
115        } else {
116            // SAFETY: borrowed data lives for `'a`
117            Some(unsafe { core::slice::from_raw_parts(self.ptr.as_ptr(), self.len()) })
118        }
119    }
120
121    /// Returns a slice borrowing from this one.
122    #[inline]
123    pub(crate) fn reborrow(&self) -> Slice<'_> {
124        Slice::borrowed(self.as_slice())
125    }
126
127    /// Converts the slice into a box, copying borrowed data.
128    #[inline]
129    pub(crate) fn into_box(self) -> Box<[u8]> {
130        if self.is_owned() {
131            let len = self.len();
132            let ptr = self.ptr.as_ptr();
133            core::mem::forget(self);
134            // SAFETY: owned data was created from a box with this pointer
135            // and length.  `self` was forgotten so the box is not freed.
136            unsafe { Box::from_raw(core::ptr::slice_from_raw_parts_mut(ptr, len)) }
137        } else {
138            Box::from(self.as_slice())
139        }
140    }
141
142    /// Converts the slice into a `Cow`, owned data is not copied.
143    #[inline]
144    pub(crate) fn into_cow(self) -> Cow<'a, [u8]> {
145        match self.borrowed_slice() {
146            Some(data) => Cow::Borrowed(data),
147            None => Cow::Owned(self.into_box().into_vec()),
148        }
149    }
150
151    #[inline]
152    pub(crate) fn from_cow(data: Cow<'a, [u8]>) -> Slice<'a> {
153        match data {
154            Cow::Borrowed(data) => Slice::borrowed(data),
155            Cow::Owned(data) => Slice::owned(data.into_boxed_slice()),
156        }
157    }
158
159    #[inline]
160    pub(crate) fn to_static(&self) -> Slice<'static> {
161        Slice::owned(Box::from(self.as_slice()))
162    }
163}
164
165impl Drop for Slice<'_> {
166    #[inline]
167    fn drop(&mut self) {
168        if self.is_owned() {
169            // SAFETY: owned data was created from a box with this pointer
170            // and length.
171            drop(unsafe {
172                Box::from_raw(core::ptr::slice_from_raw_parts_mut(
173                    self.ptr.as_ptr(),
174                    self.len(),
175                ))
176            });
177        }
178    }
179}
180
181impl Clone for Slice<'_> {
182    #[inline]
183    fn clone(&self) -> Self {
184        if self.is_owned() {
185            Slice::owned(Box::from(self.as_slice())).with_tag(self.tag())
186        } else {
187            Slice {
188                ptr: self.ptr,
189                len: self.len,
190                _marker: PhantomData,
191            }
192        }
193    }
194}
195
196/// Text of an [`Atom`](crate::Atom), borrowed or owned.
197///
198/// This is like a `Cow<'a, str>` with a more compact representation: it's
199/// two words large, owned text is a `Box<str>`.  It dereferences to `str`
200/// and converts from and into `&str`, `String` and `Cow<str>`:
201///
202/// ```
203/// use std::borrow::Cow;
204/// use deser::Text;
205///
206/// let text = Text::from("hello");
207/// assert_eq!(text, "hello");
208/// assert_eq!(text.borrowed_str(), Some("hello"));
209///
210/// let owned = Text::from(String::from("world"));
211/// assert_eq!(owned.len(), 5);
212/// assert_eq!(owned.borrowed_str(), None);
213/// assert_eq!(owned.into_cow(), Cow::<str>::Owned("world".into()));
214/// ```
215#[derive(Clone)]
216pub struct Text<'a>(Slice<'a>);
217
218impl<'a> Text<'a> {
219    /// Creates text that borrows.
220    #[inline]
221    pub const fn borrowed(text: &'a str) -> Text<'a> {
222        Text(Slice::borrowed(text.as_bytes()))
223    }
224
225    /// Creates owned text.
226    ///
227    /// The string is converted into a `Box<str>`, which reallocates if the
228    /// string has spare capacity.
229    #[inline]
230    pub fn owned<S: Into<Box<str>>>(text: S) -> Text<'a> {
231        Text(Slice::owned(text.into().into_boxed_bytes()))
232    }
233
234    /// Returns the tag stored in the text (see [`Slice::tag`]).
235    #[inline]
236    pub(crate) fn tag(&self) -> u8 {
237        self.0.tag()
238    }
239
240    /// Returns the text with a tag (see [`Slice::with_tag`]).
241    #[inline]
242    pub(crate) fn with_tag(self, tag: u8) -> Text<'a> {
243        Text(self.0.with_tag(tag))
244    }
245
246    /// Returns the text.
247    #[inline]
248    pub fn as_str(&self) -> &str {
249        // SAFETY: the data is always valid UTF-8
250        unsafe { core::str::from_utf8_unchecked(self.0.as_slice()) }
251    }
252
253    /// Returns the text if it borrows for `'a`.
254    ///
255    /// This is used by types which borrow from the data that is
256    /// deserialized (like `&'de str`).
257    #[inline]
258    pub fn borrowed_str(&self) -> Option<&'a str> {
259        // SAFETY: the data is always valid UTF-8
260        self.0
261            .borrowed_slice()
262            .map(|data| unsafe { core::str::from_utf8_unchecked(data) })
263    }
264
265    /// Returns `true` if the text borrows for `'a`.
266    #[inline]
267    pub fn is_borrowed(&self) -> bool {
268        !self.0.is_owned()
269    }
270
271    /// Returns text borrowing from this one.
272    #[inline]
273    pub fn as_borrowed(&self) -> Text<'_> {
274        Text(self.0.reborrow())
275    }
276
277    /// Makes an owned copy decoupling the lifetimes.
278    #[inline]
279    pub fn to_static(&self) -> Text<'static> {
280        Text(self.0.to_static())
281    }
282
283    /// Converts the text into a `Cow`.
284    ///
285    /// Owned text is not copied.
286    #[inline]
287    pub fn into_cow(self) -> Cow<'a, str> {
288        match self.0.into_cow() {
289            // SAFETY: the data is always valid UTF-8
290            Cow::Borrowed(data) => Cow::Borrowed(unsafe { core::str::from_utf8_unchecked(data) }),
291            Cow::Owned(data) => Cow::Owned(unsafe { String::from_utf8_unchecked(data) }),
292        }
293    }
294
295    /// Converts the text into a `String`.
296    ///
297    /// Owned text is not copied.
298    #[inline]
299    pub fn into_owned(self) -> String {
300        self.into_cow().into_owned()
301    }
302}
303
304impl Default for Text<'_> {
305    #[inline]
306    fn default() -> Self {
307        Text::borrowed("")
308    }
309}
310
311impl Deref for Text<'_> {
312    type Target = str;
313
314    #[inline]
315    fn deref(&self) -> &str {
316        self.as_str()
317    }
318}
319
320impl AsRef<str> for Text<'_> {
321    #[inline]
322    fn as_ref(&self) -> &str {
323        self.as_str()
324    }
325}
326
327impl Borrow<str> for Text<'_> {
328    #[inline]
329    fn borrow(&self) -> &str {
330        self.as_str()
331    }
332}
333
334impl<'a> From<&'a str> for Text<'a> {
335    #[inline]
336    fn from(text: &'a str) -> Text<'a> {
337        Text::borrowed(text)
338    }
339}
340
341impl<'a> From<&'a String> for Text<'a> {
342    #[inline]
343    fn from(text: &'a String) -> Text<'a> {
344        Text::borrowed(text)
345    }
346}
347
348impl From<String> for Text<'_> {
349    #[inline]
350    fn from(text: String) -> Self {
351        Text::owned(text)
352    }
353}
354
355impl From<Box<str>> for Text<'_> {
356    #[inline]
357    fn from(text: Box<str>) -> Self {
358        Text::owned(text)
359    }
360}
361
362impl<'a> From<Cow<'a, str>> for Text<'a> {
363    #[inline]
364    fn from(text: Cow<'a, str>) -> Text<'a> {
365        match text {
366            Cow::Borrowed(text) => Text::borrowed(text),
367            Cow::Owned(text) => Text::owned(text),
368        }
369    }
370}
371
372impl<'a> From<Text<'a>> for Cow<'a, str> {
373    #[inline]
374    fn from(text: Text<'a>) -> Cow<'a, str> {
375        text.into_cow()
376    }
377}
378
379impl From<Text<'_>> for String {
380    #[inline]
381    fn from(text: Text<'_>) -> String {
382        text.into_owned()
383    }
384}
385
386impl fmt::Debug for Text<'_> {
387    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
388        fmt::Debug::fmt(self.as_str(), f)
389    }
390}
391
392impl fmt::Display for Text<'_> {
393    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
394        fmt::Display::fmt(self.as_str(), f)
395    }
396}
397
398impl PartialEq for Text<'_> {
399    #[inline]
400    fn eq(&self, other: &Self) -> bool {
401        self.as_str() == other.as_str()
402    }
403}
404
405impl Eq for Text<'_> {}
406
407impl PartialOrd for Text<'_> {
408    #[inline]
409    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
410        Some(self.cmp(other))
411    }
412}
413
414impl Ord for Text<'_> {
415    #[inline]
416    fn cmp(&self, other: &Self) -> Ordering {
417        self.as_str().cmp(other.as_str())
418    }
419}
420
421impl Hash for Text<'_> {
422    #[inline]
423    fn hash<H: Hasher>(&self, state: &mut H) {
424        self.as_str().hash(state)
425    }
426}
427
428impl PartialEq<str> for Text<'_> {
429    #[inline]
430    fn eq(&self, other: &str) -> bool {
431        self.as_str() == other
432    }
433}
434
435impl PartialEq<&str> for Text<'_> {
436    #[inline]
437    fn eq(&self, other: &&str) -> bool {
438        self.as_str() == *other
439    }
440}
441
442impl PartialEq<String> for Text<'_> {
443    #[inline]
444    fn eq(&self, other: &String) -> bool {
445        self.as_str() == other
446    }
447}
448
449impl PartialEq<Text<'_>> for str {
450    #[inline]
451    fn eq(&self, other: &Text<'_>) -> bool {
452        self == other.as_str()
453    }
454}
455
456impl PartialEq<Text<'_>> for &str {
457    #[inline]
458    fn eq(&self, other: &Text<'_>) -> bool {
459        *self == other.as_str()
460    }
461}
462
463#[test]
464fn test_text() {
465    assert_eq!(core::mem::size_of::<Text>(), 16);
466    assert_eq!(core::mem::size_of::<Option<Text>>(), 16);
467
468    let borrowed = Text::from("borrowed");
469    assert!(borrowed.is_borrowed());
470    assert_eq!(borrowed.borrowed_str(), Some("borrowed"));
471    assert_eq!(borrowed.clone(), "borrowed");
472    assert_eq!(borrowed.to_static(), "borrowed");
473    assert!(!borrowed.to_static().is_borrowed());
474
475    let mut owned = String::with_capacity(64);
476    owned.push_str("owned");
477    let owned = Text::from(owned);
478    assert!(!owned.is_borrowed());
479    assert_eq!(owned.borrowed_str(), None);
480    assert_eq!(owned.as_borrowed().borrowed_str(), Some("owned"));
481    assert_eq!(owned.clone(), owned);
482    assert_eq!(owned.clone().into_owned(), "owned");
483    assert_eq!(owned.into_cow(), Cow::<str>::Owned("owned".into()));
484
485    let empty = Text::from(String::new());
486    assert_eq!(empty, "");
487    assert_eq!(empty.into_owned(), "");
488    assert_eq!(Text::default(), "");
489}
490
491#[test]
492fn test_text_tag() {
493    // the tag is not part of the text
494    for text in [Text::from("tagged"), Text::from(String::from("tagged"))] {
495        let owned = !text.is_borrowed();
496        let tagged = text.with_tag(5);
497        assert_eq!(tagged.tag(), 5);
498        assert_eq!(tagged, "tagged");
499        assert_eq!(tagged.len(), 6);
500        assert_eq!(tagged.is_borrowed(), !owned);
501        // clones keep it, conversions do not
502        assert_eq!(tagged.clone().tag(), 5);
503        assert_eq!(tagged.as_borrowed().tag(), 0);
504        assert_eq!(tagged.to_static().tag(), 0);
505        let retagged = tagged.with_tag(2);
506        assert_eq!(retagged.tag(), 2);
507        assert_eq!(retagged.clone().into_owned(), "tagged");
508        assert_eq!(retagged.with_tag(0).tag(), 0);
509    }
510}