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, Encodable, Encode, EncodeError, EncodeRef, Encoder,
14    FromWire, FromWireRef, IntoNatural, Slot, ValidationError, Wire, WireVector,
15};
16
17/// A FIDL string
18#[repr(transparent)]
19pub struct WireString<'de> {
20    vec: WireVector<'de, u8>,
21}
22
23unsafe impl Wire for WireString<'static> {
24    type Decoded<'de> = WireString<'de>;
25
26    #[inline]
27    fn zero_padding(out: &mut MaybeUninit<Self>) {
28        munge!(let Self { vec } = out);
29        WireVector::<u8>::zero_padding(vec);
30    }
31}
32
33impl WireString<'_> {
34    /// Encodes that a string is present in a slot.
35    #[inline]
36    pub fn encode_present(out: &mut MaybeUninit<Self>, len: u64) {
37        munge!(let Self { vec } = out);
38        WireVector::encode_present(vec, len);
39    }
40
41    /// Returns the length of the string in bytes.
42    #[inline]
43    pub fn len(&self) -> usize {
44        self.vec.len()
45    }
46
47    /// Returns whether the string is empty.
48    #[inline]
49    pub fn is_empty(&self) -> bool {
50        self.len() == 0
51    }
52
53    /// Returns a reference to the underlying `str`.
54    #[inline]
55    pub fn as_str(&self) -> &str {
56        unsafe { from_utf8_unchecked(self.vec.as_slice()) }
57    }
58
59    /// Validate that this string's length falls within the limit.
60    fn validate_max_len(slot: Slot<'_, Self>, limit: u64) -> Result<(), crate::ValidationError> {
61        munge!(let Self { vec } = slot);
62        match WireVector::validate_max_len(vec, limit) {
63            Ok(()) => Ok(()),
64            Err(ValidationError::VectorTooLong { count, limit }) => {
65                Err(ValidationError::StringTooLong { count, limit })
66            }
67            Err(e) => Err(e),
68        }
69    }
70}
71
72impl Constrained for WireString<'_> {
73    type Constraint = u64;
74
75    fn validate(slot: Slot<'_, Self>, constraint: u64) -> Result<(), ValidationError> {
76        Self::validate_max_len(slot, constraint)
77    }
78}
79
80impl Deref for WireString<'_> {
81    type Target = str;
82
83    #[inline]
84    fn deref(&self) -> &Self::Target {
85        self.as_str()
86    }
87}
88
89impl fmt::Debug for WireString<'_> {
90    #[inline]
91    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
92        self.as_str().fmt(f)
93    }
94}
95
96unsafe impl<D: Decoder + ?Sized> Decode<D> for WireString<'static> {
97    #[inline]
98    fn decode(slot: Slot<'_, Self>, decoder: &mut D, constraint: u64) -> Result<(), DecodeError> {
99        munge!(let Self { mut vec } = slot);
100
101        match unsafe { WireVector::decode_raw(vec.as_mut(), decoder, constraint) } {
102            Ok(()) => (),
103            Err(DecodeError::Validation(ValidationError::VectorTooLong { count, limit })) => {
104                return Err(DecodeError::Validation(ValidationError::StringTooLong {
105                    count,
106                    limit,
107                }));
108            }
109            Err(e) => {
110                return Err(e);
111            }
112        };
113        let vec = unsafe { vec.deref_unchecked() };
114
115        // Check if the string is valid ASCII (fast path)
116        if !vec.as_slice().is_ascii() {
117            // Fall back to checking if the string is valid UTF-8 (slow path)
118            // We're using `from_utf8` more like an `is_utf8` here.
119            let _ = from_utf8(vec.as_slice())?;
120        }
121
122        Ok(())
123    }
124}
125
126impl Encodable for String {
127    type Encoded = WireString<'static>;
128}
129
130unsafe impl<E: Encoder + ?Sized> Encode<E> for String {
131    #[inline]
132    fn encode(
133        self,
134        encoder: &mut E,
135        out: &mut MaybeUninit<Self::Encoded>,
136        constraint: u64,
137    ) -> Result<(), EncodeError> {
138        self.as_str().encode(encoder, out, constraint)
139    }
140}
141
142unsafe impl<E: Encoder + ?Sized> EncodeRef<E> for String {
143    #[inline]
144    fn encode_ref(
145        &self,
146        encoder: &mut E,
147        out: &mut MaybeUninit<Self::Encoded>,
148        constraint: u64,
149    ) -> Result<(), EncodeError> {
150        self.as_str().encode(encoder, out, constraint)
151    }
152}
153
154impl Encodable for &str {
155    type Encoded = WireString<'static>;
156}
157
158unsafe impl<E: Encoder + ?Sized> Encode<E> for &str {
159    #[inline]
160    fn encode(
161        self,
162        encoder: &mut E,
163        out: &mut MaybeUninit<Self::Encoded>,
164        _constraint: u64,
165    ) -> Result<(), EncodeError> {
166        encoder.write(self.as_bytes());
167        WireString::encode_present(out, self.len() as u64);
168        Ok(())
169    }
170}
171
172impl FromWire<WireString<'_>> for String {
173    #[inline]
174    fn from_wire(wire: WireString<'_>) -> Self {
175        String::from_wire_ref(&wire)
176    }
177}
178
179impl IntoNatural for WireString<'_> {
180    type Natural = String;
181}
182
183impl FromWireRef<WireString<'_>> for String {
184    #[inline]
185    fn from_wire_ref(wire: &WireString<'_>) -> Self {
186        unsafe { String::from_utf8_unchecked(Vec::from_wire_ref(&wire.vec)) }
187    }
188}