zx/
name.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 crate::sys::ZX_MAX_NAME_LEN;
6use crate::Status;
7use bstr::BStr;
8use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
9
10/// A wrapper around zircon name fields.
11///
12/// This type is ABI-compatible with the syscall interface but offers convenient ways to print and
13/// create UTF-8 strings.
14///
15/// Can be created from regular strings or byte slices.
16#[derive(
17    IntoBytes,
18    Copy,
19    Clone,
20    Default,
21    Eq,
22    KnownLayout,
23    FromBytes,
24    Hash,
25    Immutable,
26    PartialEq,
27    PartialOrd,
28    Ord,
29)]
30#[repr(transparent)]
31pub struct Name([u8; ZX_MAX_NAME_LEN]);
32
33impl Name {
34    /// Create a new name value.
35    ///
36    /// Returns an error if the string is too long to fit or contains null bytes.
37    #[inline]
38    pub const fn new(s: &str) -> Result<Self, Status> {
39        Self::from_bytes(s.as_bytes())
40    }
41
42    /// Create a new name value, truncating the input string to fit and stripping null bytes if
43    /// necessary.
44    #[inline]
45    pub const fn new_lossy(s: &str) -> Self {
46        Self::from_bytes_lossy(s.as_bytes())
47    }
48
49    /// Create a new name value from raw bytes.
50    ///
51    /// Returns an error if the slice is longer than `ZX_MAX_LEN_NAME - 1` or contains null bytes.
52    #[inline]
53    pub const fn from_bytes(b: &[u8]) -> Result<Self, Status> {
54        if b.len() >= ZX_MAX_NAME_LEN {
55            return Err(Status::INVALID_ARGS);
56        }
57
58        let mut inner = [0u8; ZX_MAX_NAME_LEN];
59        let mut i = 0;
60        while i < b.len() {
61            if b[i] == 0 {
62                return Err(Status::INVALID_ARGS);
63            }
64            inner[i] = b[i];
65            i += 1;
66        }
67
68        Ok(Self(inner))
69    }
70
71    /// Create a new name value from raw bytes, truncating the input to fit if necessary. Strips
72    /// null bytes.
73    #[inline]
74    pub const fn from_bytes_lossy(b: &[u8]) -> Self {
75        let to_copy = if b.len() <= ZX_MAX_NAME_LEN - 1 { b.len() } else { ZX_MAX_NAME_LEN - 1 };
76
77        let mut inner = [0u8; ZX_MAX_NAME_LEN];
78        let mut source_idx = 0;
79        let mut dest_idx = 0;
80        while source_idx < to_copy {
81            if b[source_idx] != 0 {
82                inner[dest_idx] = b[source_idx];
83                dest_idx += 1;
84            }
85            source_idx += 1;
86        }
87
88        Self(inner)
89    }
90
91    fn before_nulls(&self) -> &[u8] {
92        self.0.splitn(ZX_MAX_NAME_LEN - 1, |b| *b == 0).next().unwrap_or(&[])
93    }
94
95    /// Returns a raw pointer to the name for passing to syscalls.
96    pub(crate) fn as_raw(&self) -> *const u8 {
97        self.0.as_ptr()
98    }
99
100    /// Returns the length of the name before a terminating null byte.
101    #[inline]
102    pub fn len(&self) -> usize {
103        self.before_nulls().len()
104    }
105
106    /// Returns whether the name has any non-null bytes.
107    #[inline]
108    pub fn is_empty(&self) -> bool {
109        self.before_nulls().is_empty()
110    }
111
112    /// Return the non-null portion of the name as a BStr.
113    #[inline]
114    pub fn as_bstr(&self) -> &BStr {
115        BStr::new(self.before_nulls())
116    }
117}
118
119impl std::fmt::Debug for Name {
120    #[inline]
121    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
122        std::fmt::Debug::fmt(self.as_bstr(), f)
123    }
124}
125
126impl std::fmt::Display for Name {
127    #[inline]
128    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
129        std::fmt::Display::fmt(self.as_bstr(), f)
130    }
131}
132
133impl PartialEq<str> for Name {
134    #[inline]
135    fn eq(&self, other: &str) -> bool {
136        self.before_nulls() == other.as_bytes()
137    }
138}
139
140impl PartialEq<&str> for Name {
141    #[inline]
142    fn eq(&self, other: &&str) -> bool {
143        self.eq(*other)
144    }
145}
146
147impl PartialEq<Name> for str {
148    #[inline]
149    fn eq(&self, other: &Name) -> bool {
150        self.as_bytes() == other.before_nulls()
151    }
152}
153
154impl PartialEq<Name> for &str {
155    #[inline]
156    fn eq(&self, other: &Name) -> bool {
157        (*self).eq(other)
158    }
159}
160
161#[cfg(test)]
162mod tests {
163    use super::*;
164
165    #[test]
166    fn empty_name() {
167        assert_eq!(Name::new("").unwrap(), "");
168        assert_eq!(Name::new_lossy(""), "");
169    }
170
171    #[test]
172    fn short_name() {
173        assert_eq!(Name::new("v").unwrap(), "v");
174        assert_eq!(Name::new_lossy("v"), "v");
175    }
176
177    #[test]
178    fn max_len_name() {
179        let max_len_name = "a_great_maximum_length_vmo_name";
180        assert_eq!(max_len_name.len(), ZX_MAX_NAME_LEN - 1);
181        assert_eq!(Name::new(max_len_name).unwrap(), max_len_name);
182        assert_eq!(Name::new_lossy(max_len_name), max_len_name);
183    }
184
185    #[test]
186    fn too_long_name() {
187        let too_long_name = "bad_really_too_too_long_vmo_name";
188        assert_eq!(too_long_name.len(), ZX_MAX_NAME_LEN);
189        assert_eq!(Name::new(too_long_name), Err(Status::INVALID_ARGS));
190        assert_eq!(Name::new_lossy(too_long_name), "bad_really_too_too_long_vmo_nam");
191    }
192
193    #[test]
194    fn interior_null_handling() {
195        assert_eq!(Name::new("lol\0lol"), Err(Status::INVALID_ARGS));
196        assert_eq!(Name::new_lossy("lol\0lol"), "lollol");
197    }
198}