logo
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
//! An archived string representation that supports inlining short strings.

use crate::{Archived, FixedIsize, FixedUsize};
use core::{mem, ptr, slice, str};

const OFFSET_BYTES: usize = mem::size_of::<FixedIsize>();

#[derive(Clone, Copy)]
#[repr(C)]
struct OutOfLineRepr {
    len: Archived<usize>,
    // offset is always stored in little-endian format to put the sign bit at the end
    // this representation is optimized for little-endian architectures
    offset: [u8; OFFSET_BYTES],
}

/// The maximum number of bytes that can be inlined.
pub const INLINE_CAPACITY: usize = mem::size_of::<OutOfLineRepr>() - 1;

#[derive(Clone, Copy)]
#[repr(C)]
struct InlineRepr {
    bytes: [u8; INLINE_CAPACITY],
    len: u8,
}

/// An archived string representation that can inline short strings.
pub union ArchivedStringRepr {
    out_of_line: OutOfLineRepr,
    inline: InlineRepr,
}

impl ArchivedStringRepr {
    /// Returns whether the representation is inline.
    #[inline]
    pub fn is_inline(&self) -> bool {
        unsafe { self.inline.len & 0x80 == 0 }
    }

    /// Returns the offset of the representation.
    ///
    /// # Safety
    ///
    /// The internal representation must be out-of-line.
    #[inline]
    pub unsafe fn out_of_line_offset(&self) -> isize {
        FixedIsize::from_le_bytes(self.out_of_line.offset) as isize
    }

    /// Returns a pointer to the bytes of the string.
    #[inline]
    pub fn as_ptr(&self) -> *const u8 {
        unsafe {
            if self.is_inline() {
                self.inline.bytes.as_ptr()
            } else {
                (self as *const Self)
                    .cast::<u8>()
                    .offset(self.out_of_line_offset())
            }
        }
    }

    /// Returns a mutable pointer to the bytes of the string.
    #[inline]
    pub fn as_mut_ptr(&mut self) -> *mut u8 {
        unsafe {
            if self.is_inline() {
                self.inline.bytes.as_mut_ptr()
            } else {
                (self as *mut Self)
                    .cast::<u8>()
                    .offset(self.out_of_line_offset())
            }
        }
    }

    /// Returns the length of the string.
    #[inline]
    pub fn len(&self) -> usize {
        unsafe {
            if self.is_inline() {
                self.inline.len as usize
            } else {
                from_archived!(self.out_of_line.len) as usize
            }
        }
    }

    /// Returns whether the string is empty.
    #[inline]
    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }

    /// Returns a pointer to the string as a `str`.
    #[cfg(feature = "validation")]
    #[inline]
    pub fn as_str_ptr(&self) -> *const str {
        ptr_meta::from_raw_parts(self.as_ptr().cast(), self.len())
    }

    /// Returns a slice of the bytes of the string.
    #[inline]
    pub fn bytes(&self) -> &[u8] {
        unsafe { slice::from_raw_parts(self.as_ptr(), self.len()) }
    }

    /// Returns a mutable slice of the bytes of the string.
    #[inline]
    pub fn bytes_mut(&mut self) -> &mut [u8] {
        unsafe { slice::from_raw_parts_mut(self.as_mut_ptr(), self.len()) }
    }

    /// Returns a reference to the string as a `str`.
    #[inline]
    pub fn as_str(&self) -> &str {
        unsafe { str::from_utf8_unchecked(self.bytes()) }
    }

    /// Returns a mutable reference to the string as a `str`.
    #[inline]
    pub fn as_mut_str(&mut self) -> &mut str {
        unsafe { str::from_utf8_unchecked_mut(self.bytes_mut()) }
    }

    /// Emplaces a new inline representation for the given `str`.
    ///
    /// # Safety
    ///
    /// - The length of `str` must be less than or equal to [`INLINE_CAPACITY`].
    /// - `out` must point to a valid location to write the inline representation.
    #[inline]
    pub unsafe fn emplace_inline(value: &str, out: *mut Self) {
        let out_bytes = ptr::addr_of_mut!((*out).inline.bytes);
        ptr::copy_nonoverlapping(value.as_bytes().as_ptr(), out_bytes.cast(), value.len());

        let out_len = ptr::addr_of_mut!((*out).inline.len);
        *out_len = value.len() as u8;
    }

    /// Rmplaces a new out-of-line representation for the given `str`.
    ///
    /// # Safety
    ///
    /// - The length of `str` must be greater than [`INLINE_CAPACITY`].
    /// - `pos` must be the location of the representation within the archive.
    /// - `target` must be the location of the serialized bytes of the string.
    /// - `out` must point to a valid location to write the out-of-line representation.
    #[inline]
    pub unsafe fn emplace_out_of_line(value: &str, pos: usize, target: usize, out: *mut Self) {
        let out_len = ptr::addr_of_mut!((*out).out_of_line.len);
        out_len.write(to_archived!(value.len() as FixedUsize));

        let out_offset = ptr::addr_of_mut!((*out).out_of_line.offset);
        let offset = crate::rel_ptr::signed_offset(pos, target).unwrap();
        *out_offset = (offset as FixedIsize).to_le_bytes();
    }
}