Skip to main content

fidl_next_codec/wire/string/
required.rs

1// Copyright 2024 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use core::fmt;
6use core::mem::MaybeUninit;
7use core::ops::Deref;
8use core::str::{from_utf8, from_utf8_unchecked};
9
10use munge::munge;
11
12use crate::{
13    Constrained, Decode, DecodeError, Decoder, Encode, EncodeError, Encoder, FromWire, FromWireRef,
14    IntoNatural, Slot, ValidationError, Wire, wire,
15};
16
17use std::string::String as StdString;
18
19/// A FIDL string
20#[repr(transparent)]
21pub struct String<'de> {
22    vec: wire::Vector<'de, u8>,
23}
24
25// SAFETY: `String` is a `#[repr(transparent)]` wrapper around `wire::Vector<'static, u8>`, which
26// is `Wire`.
27unsafe impl Wire for String<'static> {
28    type Narrowed<'de> = String<'de>;
29
30    #[inline]
31    fn zero_padding(out: &mut MaybeUninit<Self>) {
32        munge!(let Self { vec } = out);
33        wire::Vector::<u8>::zero_padding(vec);
34    }
35}
36
37impl String<'_> {
38    /// Encodes that a string is present in a slot.
39    #[inline]
40    pub fn encode_present(out: &mut MaybeUninit<Self>, len: u64) {
41        munge!(let Self { vec } = out);
42        wire::Vector::encode_present(vec, len);
43    }
44
45    /// Returns the length of the string in bytes.
46    #[inline]
47    pub fn len(&self) -> usize {
48        self.vec.len()
49    }
50
51    /// Returns whether the string is empty.
52    #[inline]
53    pub fn is_empty(&self) -> bool {
54        self.len() == 0
55    }
56
57    /// Returns a reference to the underlying `str`.
58    #[inline]
59    pub fn as_str(&self) -> &str {
60        // SAFETY: The bytes of a decoded `String` are validated to be valid UTF-8.
61        unsafe { from_utf8_unchecked(self.vec.as_slice()) }
62    }
63
64    /// Validate that this string's length falls within the limit.
65    fn validate_max_len(slot: Slot<'_, Self>, limit: u64) -> Result<(), ValidationError> {
66        munge!(let Self { vec } = slot);
67        match wire::Vector::validate_max_len(vec, limit) {
68            Ok(()) => Ok(()),
69            Err(ValidationError::VectorTooLong { count, limit }) => {
70                Err(ValidationError::StringTooLong { count, limit })
71            }
72            Err(e) => Err(e),
73        }
74    }
75}
76
77impl Constrained for String<'_> {
78    type Constraint = u64;
79
80    fn validate(slot: Slot<'_, Self>, constraint: u64) -> Result<(), ValidationError> {
81        Self::validate_max_len(slot, constraint)
82    }
83}
84
85impl Deref for String<'_> {
86    type Target = str;
87
88    #[inline]
89    fn deref(&self) -> &Self::Target {
90        self.as_str()
91    }
92}
93
94impl fmt::Debug for String<'_> {
95    #[inline]
96    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
97        self.as_str().fmt(f)
98    }
99}
100
101impl<U: ?Sized> PartialEq<&U> for String<'_>
102where
103    for<'de> String<'de>: PartialEq<U>,
104{
105    fn eq(&self, other: &&U) -> bool {
106        self == *other
107    }
108}
109
110impl PartialEq for String<'_> {
111    fn eq(&self, other: &Self) -> bool {
112        self.as_str() == other.as_str()
113    }
114}
115
116impl PartialEq<str> for String<'_> {
117    fn eq(&self, other: &str) -> bool {
118        self.as_str() == other
119    }
120}
121
122// SAFETY: If `decode` returns `Ok`, `slot` is guaranteed to contain a valid decoded `String`
123// because it delegates to `wire::Vector::decode_raw` and validates that the decoded bytes
124// are valid UTF-8.
125unsafe impl<'de, D: Decoder<'de> + ?Sized> Decode<D> for String<'de> {
126    #[inline]
127    fn decode(slot: Slot<'_, Self>, decoder: &mut D, constraint: u64) -> Result<(), DecodeError> {
128        munge!(let Self { mut vec } = slot);
129
130        // SAFETY: `vec` is a valid slot for `Vector`.
131        match unsafe { wire::Vector::decode_raw(vec.as_mut(), decoder, constraint) } {
132            Ok(()) => (),
133            Err(DecodeError::Validation(ValidationError::VectorTooLong { count, limit })) => {
134                return Err(DecodeError::Validation(ValidationError::StringTooLong {
135                    count,
136                    limit,
137                }));
138            }
139            Err(e) => {
140                return Err(e);
141            }
142        };
143        // SAFETY: `decode_raw` succeeded, so the slot contents are valid.
144        let vec = unsafe { vec.deref_unchecked() };
145
146        // Check if the string is valid ASCII (fast path)
147        if !vec.as_slice().is_ascii() {
148            // Fall back to checking if the string is valid UTF-8 (slow path)
149            // We're using `from_utf8` more like an `is_utf8` here.
150            let _ = from_utf8(vec.as_slice())?;
151        }
152
153        Ok(())
154    }
155}
156
157// SAFETY: Delegates to `<&str>::encode` which is safe and fully initializes the output.
158unsafe impl<E: Encoder + ?Sized> Encode<String<'static>, E> for StdString {
159    #[inline]
160    fn encode(
161        self,
162        encoder: &mut E,
163        out: &mut MaybeUninit<String<'static>>,
164        constraint: u64,
165    ) -> Result<(), EncodeError> {
166        self.as_str().encode(encoder, out, constraint)
167    }
168}
169
170// SAFETY: Delegates to `<&str>::encode` which is safe and fully initializes the output.
171unsafe impl<E: Encoder + ?Sized> Encode<String<'static>, E> for &StdString {
172    #[inline]
173    fn encode(
174        self,
175        encoder: &mut E,
176        out: &mut MaybeUninit<String<'static>>,
177        constraint: u64,
178    ) -> Result<(), EncodeError> {
179        self.as_str().encode(encoder, out, constraint)
180    }
181}
182
183// SAFETY: `String` has no padding. `encode` initializes the output fully by writing
184// the string bytes to the encoder and calling `String::encode_present`.
185unsafe impl<E: Encoder + ?Sized> Encode<String<'static>, E> for &str {
186    #[inline]
187    fn encode(
188        self,
189        encoder: &mut E,
190        out: &mut MaybeUninit<String<'static>>,
191        _constraint: u64,
192    ) -> Result<(), EncodeError> {
193        encoder.write(self.as_bytes());
194        String::encode_present(out, self.len() as u64);
195        Ok(())
196    }
197}
198
199impl FromWire<String<'_>> for StdString {
200    #[inline]
201    fn from_wire(wire: String<'_>) -> Self {
202        StdString::from_wire_ref(&wire)
203    }
204}
205
206impl IntoNatural for String<'_> {
207    type Natural = StdString;
208}
209
210impl FromWireRef<String<'_>> for StdString {
211    #[inline]
212    fn from_wire_ref(wire: &String<'_>) -> Self {
213        // SAFETY: The bytes of a decoded `String` are validated to be valid UTF-8.
214        unsafe { StdString::from_utf8_unchecked(Vec::from_wire_ref(&wire.vec)) }
215    }
216}
217
218#[cfg(test)]
219mod tests {
220    use crate::{DecoderExt as _, EncoderExt as _, chunks, wire};
221
222    #[test]
223    fn decode_string() {
224        assert_eq!(
225            chunks![
226                0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
227                0xff, 0xff, 0x30, 0x31, 0x32, 0x33, 0x00, 0x00, 0x00, 0x00,
228            ]
229            .as_mut_slice()
230            .decode_with_constraint::<wire::String<'_>>(1000)
231            .unwrap(),
232            "0123",
233        );
234        assert_eq!(
235            chunks![
236                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
237                0xff, 0xff,
238            ]
239            .as_mut_slice()
240            .decode_with_constraint::<wire::String<'_>>(1000)
241            .unwrap(),
242            "",
243        );
244    }
245
246    #[test]
247    fn encode_string() {
248        assert_eq!(
249            Vec::encode_with_constraint(Some("0123".to_string()), 1000).unwrap(),
250            chunks![
251                0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
252                0xff, 0xff, 0x30, 0x31, 0x32, 0x33, 0x00, 0x00, 0x00, 0x00,
253            ],
254        );
255        assert_eq!(
256            Vec::encode_with_constraint(Some(String::new()), 1000).unwrap(),
257            chunks![
258                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
259                0xff, 0xff,
260            ],
261        );
262    }
263}