1use std::cell::RefCell;
11use std::io::Read;
12
13use crate::line_index::LineIndex;
14
15#[derive(Debug)]
17pub struct NullTerminatedBuf(Vec<u8>);
18
19impl NullTerminatedBuf {
20 pub fn from_reader(reader: &mut dyn Read) -> Result<NullTerminatedBuf, std::io::Error> {
22 let mut v = Vec::<u8>::new();
23 reader.read_to_end(&mut v)?;
24 v.push(0);
25
26 Ok(NullTerminatedBuf(v))
27 }
28
29 pub fn from_file(f: &'_ mut std::fs::File) -> Result<NullTerminatedBuf, std::io::Error> {
31 Self::from_reader(f)
39 }
40
41 pub fn from_slice_copy(s: &[u8]) -> NullTerminatedBuf {
43 let mut v = Vec::with_capacity(s.len() + 1);
44 v.extend_from_slice(s);
45 v.push(0);
46 NullTerminatedBuf(v)
47 }
48
49 pub fn from_slice_check(s: &[u8]) -> NullTerminatedBuf {
51 Self::from_slice_copy(if let [head @ .., 0] = s { head } else { s })
52 }
53
54 pub fn from_str_copy(s: &str) -> NullTerminatedBuf {
56 Self::from_slice_copy(s.as_bytes())
57 }
58
59 pub fn from_str_check(s: &str) -> NullTerminatedBuf {
61 Self::from_slice_check(s.as_bytes())
62 }
63
64 pub fn len(&self) -> usize {
66 self.0.len()
67 }
68
69 pub fn is_empty(&self) -> bool {
72 false
73 }
74
75 pub fn as_bytes(&self) -> &[u8] {
76 self.0.as_slice()
77 }
78}
79
80impl AsRef<[u8]> for NullTerminatedBuf {
81 fn as_ref(&self) -> &[u8] {
82 self.as_bytes()
83 }
84}
85
86pub struct SourceBuffer {
90 name: String,
91 buf: NullTerminatedBuf,
92 line_index: RefCell<Option<LineIndex>>,
95}
96
97impl SourceBuffer {
98 pub fn from_str(name: impl Into<String>, contents: &str) -> SourceBuffer {
99 SourceBuffer {
100 name: name.into(),
101 buf: NullTerminatedBuf::from_str_copy(contents),
102 line_index: RefCell::new(None),
103 }
104 }
105
106 pub fn from_slice_check(name: impl Into<String>, contents: &[u8]) -> SourceBuffer {
107 SourceBuffer {
108 name: name.into(),
109 buf: NullTerminatedBuf::from_slice_check(contents),
110 line_index: RefCell::new(None),
111 }
112 }
113
114 pub fn name(&self) -> &str {
115 &self.name
116 }
117
118 pub fn bytes(&self) -> &[u8] {
120 let raw = self.buf.as_bytes();
121 &raw[..raw.len() - 1]
122 }
123
124 pub fn raw(&self) -> &[u8] {
126 self.buf.as_bytes()
127 }
128
129 pub fn with_line_index<R>(&self, f: impl FnOnce(&LineIndex, &[u8]) -> R) -> R {
131 {
132 let mut slot = self.line_index.borrow_mut();
133 if slot.is_none() {
134 *slot = Some(LineIndex::build(self.bytes()));
135 }
136 }
137 let slot = self.line_index.borrow();
138 f(slot.as_ref().unwrap(), self.bytes())
139 }
140}
141
142#[cfg(test)]
143mod tests {
144 use super::*;
145
146 #[test]
147 fn null_terminated_and_named() {
148 let b = SourceBuffer::from_str("foo.js", "abc");
149 assert_eq!(b.name(), "foo.js");
150 assert_eq!(b.bytes(), b"abc");
152 assert_eq!(b.raw()[b.raw().len() - 1], 0u8);
153 }
154
155 #[test]
156 fn already_terminated_input_not_doubled() {
157 let b = SourceBuffer::from_slice_check("x", b"ab\0");
158 assert_eq!(b.bytes(), b"ab");
159 }
160
161 #[test]
162 fn empty_input_gives_empty_bytes() {
163 let b = SourceBuffer::from_str("empty.js", "");
165 assert_eq!(b.bytes(), b"");
166 assert_eq!(b.raw(), b"\0");
167 assert_eq!(b.raw().len(), b.bytes().len() + 1);
168 }
169}