1use std::borrow::Cow;
2use std::str;
3
4#[derive(Debug, Clone, Copy)]
7pub struct RawData<'a> {
8 data: &'a [u8],
9}
10
11impl<'a> std::ops::Deref for RawData<'a> {
12 type Target = [u8];
13
14 #[inline]
15 fn deref(&self) -> &[u8] {
16 self.data
17 }
18}
19
20impl<'a> RawData<'a> {
21 #[inline]
23 pub fn new(data: &'a [u8]) -> Self {
24 Self { data }
25 }
26
27 #[inline]
29 pub fn substring(&self, start: usize) -> Result<&'a str, str::Utf8Error> {
30 self.substring_with(start, str::from_utf8)
31 }
32
33 #[inline]
35 pub fn substring_lossy(&self, start: usize) -> Cow<'a, str> {
36 self.substring_with(start, String::from_utf8_lossy)
37 }
38
39 #[inline]
41 pub fn substring_raw(&self, start: usize) -> &'a [u8] {
42 self.substring_with(start, std::convert::identity)
43 }
44
45 #[inline]
51 pub unsafe fn substring_unchecked(&self, start: usize) -> &'a str {
52 self.substring_with(start, |bytes| str::from_utf8_unchecked(bytes))
53 }
54
55 fn substring_with<T>(&self, start: usize, f: impl FnOnce(&'a [u8]) -> T) -> T {
56 let suffix = &self.data[start..];
57 match suffix.iter().position(|&c| c == 0) {
58 Some(idx) => f(&suffix[..idx]),
59 None => f(suffix),
60 }
61 }
62
63 #[inline]
65 pub fn as_bytes(&self) -> &'a [u8] {
66 self.data
67 }
68}
69
70#[cfg(test)]
71mod tests {
72 use super::*;
73
74 #[test]
75 fn empty() {
76 let data: &[u8] = b"";
77 let raw_data = RawData::new(data);
78 assert_eq!(raw_data.substring(0), Ok(""));
79 assert_eq!(raw_data.substring_lossy(0), "");
80 assert_eq!(raw_data.substring_raw(0), b"");
81 assert_eq!(unsafe { raw_data.substring_unchecked(0) }, "");
82 }
83
84 #[test]
85 fn last_without_terminator() {
86 let data: &[u8] = b"abc";
87 let raw_data = RawData::new(data);
88 assert_eq!(raw_data.substring(1), Ok("bc"));
89 assert_eq!(raw_data.substring_lossy(1), "bc");
90 assert_eq!(raw_data.substring_raw(1), b"bc");
91 assert_eq!(unsafe { raw_data.substring_unchecked(1) }, "bc");
92 }
93
94 #[test]
95 fn until_terminator() {
96 let data: &[u8] = b"ab\0c";
97 let raw_data = RawData::new(data);
98 assert_eq!(raw_data.substring(1), Ok("b"));
99 assert_eq!(raw_data.substring_lossy(1), "b");
100 assert_eq!(raw_data.substring_raw(1), b"b");
101 assert_eq!(unsafe { raw_data.substring_unchecked(1) }, "b");
102 }
103
104 #[test]
105 fn invalid_utf8() {
106 let data: &[u8] = b"ab\xF0\x90\x80\0c";
107 let raw_data = RawData::new(data);
108 assert!(raw_data.substring(1).is_err());
109 assert_eq!(raw_data.substring_lossy(1), "b�");
110 assert_eq!(raw_data.substring_raw(1), b"b\xF0\x90\x80");
111 }
112}