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