1use std::{cmp, fmt, mem, ops, str};
6
7use crate::util::{split_f, FromBytes};
8
9#[derive(Eq, Ord, Hash)]
13pub struct CStr {
14 bytes: [u8],
15}
16
17impl CStr {
18 pub fn empty() -> &'static CStr {
20 unsafe { CStr::from_bytes_unchecked(&[0]) }
21 }
22 pub fn from_bytes(bytes: &[u8]) -> Option<&CStr> {
40 let len = bytes.iter().position(|&byte| byte == 0)?;
41 Some(unsafe { CStr::from_bytes_unchecked(bytes.get_unchecked(..len + 1)) })
42 }
43 pub unsafe fn from_bytes_unchecked(bytes: &[u8]) -> &CStr {
49 mem::transmute(bytes)
50 }
51 pub fn c_str(&self) -> &[u8] {
53 &self.bytes
54 }
55 pub fn to_str(&self) -> Result<&str, str::Utf8Error> {
57 str::from_utf8(self.as_ref())
58 }
59}
60
61impl FromBytes for CStr {
62 const MIN_SIZE_OF: usize = 0;
63 const ALIGN_OF: usize = 1;
64 unsafe fn from_bytes(bytes: &[u8]) -> Option<&CStr> {
65 CStr::from_bytes(bytes)
66 }
67}
68
69impl<T: AsRef<[u8]> + ?Sized> PartialEq<T> for CStr {
72 fn eq(&self, rhs: &T) -> bool {
73 self.as_ref() == rhs.as_ref()
74 }
75}
76impl<T: AsRef<[u8]> + ?Sized> PartialOrd<T> for CStr {
77 fn partial_cmp(&self, rhs: &T) -> Option<cmp::Ordering> {
78 self.as_ref().partial_cmp(rhs.as_ref())
79 }
80}
81
82impl ops::Deref for CStr {
85 type Target = [u8];
86 fn deref(&self) -> &[u8] {
87 self.as_ref()
88 }
89}
90impl AsRef<[u8]> for CStr {
91 fn as_ref(&self) -> &[u8] {
92 let len = self.bytes.len() - 1;
94 unsafe { self.bytes.get_unchecked(..len) }
95 }
96}
97
98impl fmt::Debug for CStr {
102 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
103 f.write_str("\"")?;
104 let mut bytes = self.as_ref();
105 while bytes.len() > 0 {
106 let byte = bytes[0];
107 match byte {
108 b'\0' => {
109 bytes = &bytes[1..];
110 f.write_str("\\0")?;
111 },
112 b'\n' => {
113 bytes = &bytes[1..];
114 f.write_str("\\n")?;
115 },
116 b'\r' => {
117 bytes = &bytes[1..];
118 f.write_str("\\r")?;
119 },
120 b'\t' => {
121 bytes = &bytes[1..];
122 f.write_str("\\t")?;
123 },
124 b'"' => {
125 bytes = &bytes[1..];
126 f.write_str("\\\"")?;
127 },
128 b'\\' => {
129 bytes = &bytes[1..];
130 f.write_str("\\\\")?;
131 },
132 0x20...0x7E => {
133 let (s, tail) = split_f(bytes, |&byte| byte < 0x20 || byte >= 0x80 || byte == b'"' || byte == b'\\');
134 bytes = tail;
135 let s = unsafe { str::from_utf8_unchecked(s) };
136 f.write_str(s)?;
137 },
138 _ => {
139 let (s, tail) = split_f(bytes, |&byte| byte >= 0x20 && byte < 0x80);
140 bytes = tail;
141 for &byte in s {
142 write!(f, "\\x{:02X}", byte)?;
143 }
144 },
145 };
146 }
147 f.write_str("\"")
148 }
149}
150impl fmt::Display for CStr {
151 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
152 let mut bytes = self.as_ref();
153 while bytes.len() > 0 {
154 let byte = bytes[0];
155 if byte < 0x80 {
157 let (s, tail) = split_f(bytes, |&byte| byte >= 0x80);
158 bytes = tail;
159 let s = unsafe { str::from_utf8_unchecked(s) };
160 f.write_str(s)?;
161 }
162 else {
164 let (s, tail) = split_f(bytes, |&byte| byte < 0x80);
165 bytes = tail;
166 for &byte in s {
167 write!(f, "\\x{:02X}", byte)?;
168 }
169 }
170 }
171 Ok(())
172 }
173}
174
175#[cfg(feature = "serde")]
176impl serde::Serialize for CStr {
177 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
178 serializer.collect_str(self)
179 }
180}
181
182#[cfg(test)]
185mod tests {
186 use super::CStr;
187
188 #[test]
189 fn from_bytes() {
190 assert_eq!(CStr::from_bytes(b"this is a c str\0").unwrap().c_str(), b"this is a c str\0");
191 assert_eq!(CStr::from_bytes(b"no nul terminator"), None);
192 assert_eq!(CStr::from_bytes(b"valid utf8\0").unwrap().to_str(), Ok("valid utf8"));
193 assert_eq!(CStr::from_bytes(b"length is eighteen\0").unwrap().len(), 18);
194 assert_eq!(CStr::from_bytes(b"length is eighteen\0").unwrap().len(), 18);
195 }
196
197 #[test]
198 fn fmt() {
199 assert_eq!(format!("{}", unsafe { CStr::from_bytes_unchecked(b"\tabc\n\xFFhello\x80world\0") }), "\tabc\n\\xFFhello\\x80world");
200 assert_eq!(format!("{:?}", unsafe { CStr::from_bytes_unchecked(b"\tabc\n\xFFhello\x80world\0") }), r#""\tabc\n\xFFhello\x80world""#);
201 }
202}