non_zero_byte_slice/
lib.rs1use std::{
2 borrow::{Borrow, Cow, ToOwned},
3 convert::TryFrom,
4 error::Error,
5 ffi::{CStr, CString},
6 fmt,
7 num::NonZeroU8,
8 ops::Deref,
9};
10
11use serde::Serialize;
12
13#[derive(Debug, Eq, PartialEq, Hash, Serialize)]
14#[repr(transparent)]
15pub struct NonZeroByteSlice([u8]);
16
17impl NonZeroByteSlice {
18 pub const fn new(bytes: &[u8]) -> Option<&Self> {
19 let mut i = 0;
20 while i < bytes.len() {
21 if bytes[i] == 0 {
22 return None;
23 }
24 i += 1;
25 }
26
27 Some(unsafe { Self::new_unchecked(bytes) })
29 }
30
31 pub const unsafe fn new_unchecked(bytes: &[u8]) -> &Self {
35 &*(bytes as *const [u8] as *const Self)
36 }
37
38 pub const fn into_inner(&self) -> &[u8] {
39 &self.0
40 }
41}
42
43#[derive(Debug)]
45pub struct NullByteError;
46
47impl fmt::Display for NullByteError {
48 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49 write!(f, "NullByteError")
50 }
51}
52
53impl Error for NullByteError {}
54
55impl<'a> TryFrom<&'a str> for &'a NonZeroByteSlice {
56 type Error = NullByteError;
57
58 fn try_from(s: &'a str) -> Result<Self, Self::Error> {
59 NonZeroByteSlice::new(s.as_bytes()).ok_or(NullByteError)
60 }
61}
62
63impl<'a> From<&'a CStr> for &'a NonZeroByteSlice {
64 fn from(s: &'a CStr) -> Self {
65 unsafe { NonZeroByteSlice::new_unchecked(s.to_bytes()) }
67 }
68}
69
70impl ToOwned for NonZeroByteSlice {
71 type Owned = NonZeroByteVec;
72
73 fn to_owned(&self) -> Self::Owned {
74 self.into()
75 }
76}
77
78#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize)]
79#[repr(transparent)]
80pub struct NonZeroByteVec(Vec<u8>);
81
82impl NonZeroByteVec {
83 pub fn new(bytes: Vec<u8>) -> Option<Self> {
84 for byte in bytes.iter() {
85 if *byte == 0 {
86 return None;
87 }
88 }
89
90 Some(Self(bytes))
91 }
92
93 pub fn from_bytes_remove_nul(mut bytes: Vec<u8>) -> Self {
94 bytes.retain(|byte| *byte != b'\0');
95 Self(bytes)
96 }
97
98 pub const unsafe fn new_unchecked(bytes: Vec<u8>) -> Self {
102 Self(bytes)
103 }
104
105 pub fn from_slice(slice: &NonZeroByteSlice) -> Self {
106 Self(slice.into_inner().into())
107 }
108
109 pub fn push(&mut self, byte: NonZeroU8) {
110 self.0.push(byte.get())
111 }
112
113 pub fn from_bytes_slice_lossy(slice: &[u8]) -> Cow<'_, NonZeroByteSlice> {
114 NonZeroByteSlice::new(slice)
115 .map(Cow::Borrowed)
116 .unwrap_or_else(|| {
117 let bytes: Vec<u8> = slice
118 .iter()
119 .copied()
120 .filter(|byte| *byte != b'\0')
121 .collect();
122
123 Cow::Owned(unsafe { Self::new_unchecked(bytes) })
125 })
126 }
127}
128
129impl From<&NonZeroByteSlice> for NonZeroByteVec {
130 fn from(slice: &NonZeroByteSlice) -> Self {
131 Self::from_slice(slice)
132 }
133}
134
135impl TryFrom<String> for NonZeroByteVec {
136 type Error = NullByteError;
137
138 fn try_from(s: String) -> Result<Self, Self::Error> {
139 Self::new(s.into_bytes()).ok_or(NullByteError)
140 }
141}
142
143impl From<CString> for NonZeroByteVec {
144 fn from(s: CString) -> Self {
145 unsafe { Self::new_unchecked(s.into_bytes()) }
147 }
148}
149
150impl Deref for NonZeroByteVec {
151 type Target = NonZeroByteSlice;
152
153 fn deref(&self) -> &Self::Target {
154 unsafe { NonZeroByteSlice::new_unchecked(&self.0) }
156 }
157}
158
159impl Borrow<NonZeroByteSlice> for NonZeroByteVec {
160 fn borrow(&self) -> &NonZeroByteSlice {
161 self.deref()
162 }
163}
164
165#[cfg(test)]
166mod tests {
167 use super::*;
168
169 #[test]
170 fn test_byte_slice_with_zero() {
171 let mut vec: Vec<_> = (0..9).collect();
172 vec.push(0);
173
174 let option = NonZeroByteSlice::new(&vec);
175 debug_assert!(option.is_none(), "{:#?}", option);
176 }
177
178 #[test]
179 fn test_byte_slice_without_zero() {
180 let vec: Vec<_> = (1..102).collect();
181 NonZeroByteSlice::new(&vec).unwrap();
182 }
183
184 #[test]
185 fn test_byte_vec_without_zero() {
186 let vec: Vec<_> = (1..102).collect();
187 NonZeroByteVec::new(vec).unwrap();
188 }
189
190 #[test]
191 fn test_byte_vec_from_bytes_remove_nul_zero() {
192 let mut vec: Vec<_> = (0..3).collect();
193 vec.push(0);
194 assert_eq!(NonZeroByteVec::from_bytes_remove_nul(vec).0, vec![1, 2]);
195 }
196
197 #[test]
198 fn test_from_bytes_slice_lossy() {
199 let non_zero_bytes = b"1234x4r2ex";
200 assert_eq!(
201 NonZeroByteVec::from_bytes_slice_lossy(non_zero_bytes),
202 Cow::Borrowed(NonZeroByteSlice::new(non_zero_bytes).unwrap()),
203 );
204
205 let bytes_with_zero = b"\x00123x'1\x0023x\0";
206 assert_eq!(
207 NonZeroByteVec::from_bytes_slice_lossy(bytes_with_zero),
208 Cow::Owned(NonZeroByteVec::from_bytes_remove_nul(
209 bytes_with_zero.to_vec()
210 )),
211 );
212 }
213}