1use core::mem::MaybeUninit;
2
3use crate::{
4 builder::Builder, errors::ErrorKind, slice_helpers::SliceWithStartOffset, traits::*, Cursor,
5 Offset,
6};
7
8impl WriteAsOffset<str> for str {
9 #[inline]
10 #[allow(clippy::let_and_return)]
11 fn prepare(&self, builder: &mut Builder) -> Offset<str> {
12 #[cfg(feature = "string-cache")]
13 let hash = {
14 let hash = builder.string_cache.hash(self.as_bytes());
15 if let Some(offset) =
16 builder
17 .string_cache
18 .get(builder.inner.as_slice(), hash, self.as_bytes())
19 {
20 return offset.into();
21 }
22 hash
23 };
24
25 let size_including_len_and_null = self.len().checked_add(5).unwrap();
26 unsafe {
28 builder.write_with(
29 size_including_len_and_null,
30 u32::ALIGNMENT_MASK,
31 |buffer_position, bytes| {
32 let bytes = bytes.as_mut_ptr();
33
34 (self.len() as u32).write(
35 Cursor::new(&mut *(bytes as *mut [MaybeUninit<u8>; 4])),
36 buffer_position,
37 );
38 core::ptr::copy_nonoverlapping(
39 self.as_bytes().as_ptr() as *const MaybeUninit<u8>,
40 bytes.add(4),
41 self.len(),
42 );
43 bytes.add(4 + self.len()).write(MaybeUninit::new(0));
44 },
45 )
46 }
47 let offset = builder.current_offset();
48
49 #[cfg(feature = "string-cache")]
50 builder
51 .string_cache
52 .insert(hash, offset.into(), builder.inner.as_slice());
53
54 offset
55 }
56}
57
58impl WriteAs<Offset<str>> for str {
59 type Prepared = Offset<str>;
60
61 #[inline]
62 fn prepare(&self, builder: &mut Builder) -> Offset<str> {
63 WriteAsOffset::prepare(self, builder)
64 }
65}
66
67impl WriteAsOptional<Offset<str>> for str {
68 type Prepared = Offset<str>;
69 #[inline]
70 fn prepare(&self, builder: &mut Builder) -> Option<Offset<str>> {
71 Some(WriteAsOffset::prepare(self, builder))
72 }
73}
74
75impl WriteAsDefault<Offset<str>, str> for str {
76 type Prepared = Offset<str>;
77
78 #[inline]
79 fn prepare(&self, builder: &mut Builder, default: &str) -> Option<Offset<str>> {
80 if self == default {
81 None
82 } else {
83 Some(WriteAsOffset::prepare(self, builder))
84 }
85 }
86}
87
88impl<'buf> TableRead<'buf> for &'buf str {
89 fn from_buffer(
90 buffer: SliceWithStartOffset<'buf>,
91 offset: usize,
92 ) -> core::result::Result<Self, ErrorKind> {
93 let (buffer, len) = super::array_from_buffer(buffer, offset)?;
94 #[cfg(feature = "extra-validation")]
95 if buffer.as_slice().get(len) != Some(&0) {
96 return Err(ErrorKind::MissingNullTerminator);
97 }
98 let slice = buffer
99 .as_slice()
100 .get(..len)
101 .ok_or(ErrorKind::InvalidLength)?;
102 Ok(core::str::from_utf8(slice)?)
103 }
104}
105
106impl<'buf> VectorReadInner<'buf> for &'buf str {
107 type Error = crate::errors::Error;
108
109 const STRIDE: usize = 4;
110 #[inline]
111 unsafe fn from_buffer(
112 buffer: SliceWithStartOffset<'buf>,
113 offset: usize,
114 ) -> crate::Result<&'buf str> {
115 let add_context =
116 |e: ErrorKind| e.with_error_location("[str]", "get", buffer.offset_from_start);
117 let (slice, len) = super::array_from_buffer(buffer, offset).map_err(add_context)?;
118 #[cfg(feature = "extra-validation")]
119 if slice.as_slice().get(len) != Some(&0) {
120 return Err(add_context(ErrorKind::MissingNullTerminator));
121 }
122 let slice = slice
123 .as_slice()
124 .get(..len)
125 .ok_or(ErrorKind::InvalidLength)
126 .map_err(add_context)?;
127 let str = core::str::from_utf8(slice)
128 .map_err(|source| ErrorKind::InvalidUtf8 { source })
129 .map_err(add_context)?;
130 Ok(str)
131 }
132}
133
134unsafe impl VectorWrite<Offset<str>> for str {
137 type Value = Offset<str>;
138
139 const STRIDE: usize = 4;
140 #[inline]
141 fn prepare(&self, builder: &mut Builder) -> Self::Value {
142 WriteAs::prepare(self, builder)
143 }
144
145 #[inline]
146 unsafe fn write_values(
147 values: &[Offset<str>],
148 bytes: *mut MaybeUninit<u8>,
149 buffer_position: u32,
150 ) {
151 let bytes = bytes as *mut [MaybeUninit<u8>; 4];
152 for (i, v) in values.iter().enumerate() {
153 v.write(
154 Cursor::new(&mut *bytes.add(i)),
155 buffer_position - (4 * i) as u32,
156 );
157 }
158 }
159}