1use crate::sys::ZX_MAX_NAME_LEN;
6use crate::Status;
7use bstr::BStr;
8use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
9
10#[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 #[inline]
38 pub const fn new(s: &str) -> Result<Self, Status> {
39 Self::from_bytes(s.as_bytes())
40 }
41
42 #[inline]
45 pub const fn new_lossy(s: &str) -> Self {
46 Self::from_bytes_lossy(s.as_bytes())
47 }
48
49 #[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 #[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 pub(crate) fn as_raw(&self) -> *const u8 {
97 self.0.as_ptr()
98 }
99
100 #[inline]
102 pub fn len(&self) -> usize {
103 self.before_nulls().len()
104 }
105
106 #[inline]
108 pub fn is_empty(&self) -> bool {
109 self.before_nulls().is_empty()
110 }
111
112 #[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}