Skip to main content

fastnbt/
input.rs

1use std::{borrow::Cow, io::Read, ops::Range};
2
3use byteorder::{BigEndian, ReadBytesExt};
4
5use crate::{
6    error::{Error, Result},
7    Tag,
8};
9
10mod private {
11    // Only this crate can implement this trait. Other traits can inherit from
12    // Sealed in order to prevent other crates from creating implementations.
13    pub trait Sealed {}
14}
15
16fn try_size(size: i32, multiplier: usize) -> Result<usize> {
17    let size: usize = size
18        .try_into()
19        .map_err(|_| Error::bespoke("size was negative".to_string()))?;
20
21    size.checked_mul(multiplier)
22        .ok_or_else(|| Error::bespoke("size too large".to_string()))
23}
24pub enum Reference<'b, 'c, T>
25where
26    T: ?Sized + 'static,
27{
28    Borrowed(&'b T),
29    Copied(&'c T),
30}
31
32impl<'b, 'c> AsRef<[u8]> for Reference<'b, 'c, [u8]> {
33    fn as_ref(&self) -> &[u8] {
34        match self {
35            Reference::Borrowed(bs) => bs,
36            Reference::Copied(bs) => bs,
37        }
38    }
39}
40
41pub trait Input<'de>: private::Sealed {
42    #[doc(hidden)]
43    fn consume_byte(&mut self) -> Result<u8>;
44
45    // #[doc(hidden)]
46    // fn discard(&mut self);
47
48    #[doc(hidden)]
49    fn ignore_str(&mut self) -> Result<()>;
50
51    #[doc(hidden)]
52    fn ignore_bytes(&mut self, size: usize) -> Result<()>;
53
54    fn consume_tag(&mut self) -> Result<Tag> {
55        let tag = self.consume_byte()?;
56        Tag::try_from(tag).map_err(|_| Error::invalid_tag(tag))
57    }
58
59    fn consume_str<'s>(&'s mut self, scratch: &'s mut Vec<u8>) -> Result<Reference<'de, 's, str>>;
60
61    fn consume_bytes<'s>(
62        &'s mut self,
63        n: usize,
64        scratch: &'s mut Vec<u8>,
65    ) -> Result<Reference<'de, 's, [u8]>>;
66
67    fn consume_i16(&mut self) -> Result<i16>;
68    fn consume_i32(&mut self) -> Result<i32>;
69    fn consume_i64(&mut self) -> Result<i64>;
70    fn consume_f32(&mut self) -> Result<f32>;
71    fn consume_f64(&mut self) -> Result<f64>;
72
73    fn ignore_value(&mut self, tag: Tag) -> Result<()> {
74        match tag {
75            Tag::Byte => {
76                self.consume_byte()?;
77            }
78            Tag::Short => {
79                self.consume_i16()?;
80            }
81            Tag::Int => {
82                self.consume_i32()?;
83            }
84            Tag::Long => {
85                self.consume_i64()?;
86            }
87            Tag::Float => {
88                self.consume_f32()?;
89            }
90            Tag::Double => {
91                self.consume_f64()?;
92            }
93            Tag::String => {
94                self.ignore_str()?;
95            }
96            Tag::ByteArray => {
97                let size = self.consume_i32()? as usize;
98                self.ignore_bytes(size)?;
99            }
100            Tag::IntArray => {
101                let size = self.consume_i32()?;
102                self.ignore_bytes(try_size(size, std::mem::size_of::<i32>())?)?;
103            }
104            Tag::LongArray => {
105                let size = self.consume_i32()?;
106                self.ignore_bytes(try_size(size, std::mem::size_of::<i64>())?)?;
107            }
108            Tag::Compound => {
109                // Need to loop and ignore each value until we reach an end tag.
110
111                // we need to enter the compound, then ignore it's value.
112                loop {
113                    let tag = self.consume_tag()?;
114                    if tag == Tag::End {
115                        break;
116                    }
117
118                    // consume the name.
119                    self.ignore_str()?;
120                    self.ignore_value(tag)?;
121                }
122            }
123            Tag::List => {
124                let element_tag = self.consume_tag()?;
125                let size = self.consume_i32()?;
126                for _ in 0..size {
127                    self.ignore_value(element_tag)?;
128                }
129            }
130            Tag::End => {
131                // If we are trying to ignore a list of empty compounds, that
132                // list might be indicated by a series of End tags. If this
133                // occurs then we should end the Compound branch of this match
134                // statement, where the end tag will be consumed. So we should
135                // never reach here.
136                //
137                // TODO: Write an explicit test for ignored list of compound.
138                unreachable!()
139            }
140        }
141
142        Ok(())
143    }
144}
145
146pub struct Slice<'de> {
147    pub(crate) data: &'de [u8],
148}
149
150impl<'de> private::Sealed for Slice<'de> {}
151impl<'de> Slice<'de> {
152    fn consume(&mut self, r: Range<usize>) -> Result<&'de [u8]> {
153        if r.end <= self.data.len() {
154            let ret = &self.data[r.start..r.end];
155            self.data = &self.data[r.end..];
156            Ok(ret)
157        } else {
158            Err(Error::unexpected_eof())
159        }
160    }
161}
162
163impl<'de> Input<'de> for Slice<'de> {
164    fn consume_byte(&mut self) -> Result<u8> {
165        Ok(self.consume(0..1)?[0])
166    }
167
168    fn ignore_str(&mut self) -> Result<()> {
169        let len = self.consume(0..2)?.read_u16::<BigEndian>()? as usize;
170        self.consume(0..len).map(|_| ())
171    }
172
173    fn consume_str<'s>(&'s mut self, scratch: &'s mut Vec<u8>) -> Result<Reference<'de, 's, str>> {
174        let len = self.consume(0..2)?.read_u16::<BigEndian>()? as usize;
175        let str = self.consume(0..len)?;
176        let str = cesu8::from_java_cesu8(str).map_err(|_| Error::nonunicode_string(str))?;
177
178        Ok(match str {
179            Cow::Borrowed(str) => Reference::Borrowed(str),
180            Cow::Owned(str) => {
181                *scratch = str.into_bytes();
182                // we just converted scratch into the bytes of a string, so it
183                // definitely utf8.
184                Reference::Copied(unsafe { std::str::from_utf8_unchecked(scratch) })
185            }
186        })
187    }
188
189    fn consume_bytes<'s>(
190        &'s mut self,
191        n: usize,
192        _scratch: &'s mut Vec<u8>,
193    ) -> Result<Reference<'de, 's, [u8]>> {
194        let bs = self.consume(0..n)?;
195        Ok(Reference::Borrowed(bs))
196    }
197
198    fn consume_i16(&mut self) -> Result<i16> {
199        let mut bs = self.consume(0..std::mem::size_of::<i16>())?;
200        Ok(bs.read_i16::<BigEndian>()?)
201    }
202
203    fn consume_i32(&mut self) -> Result<i32> {
204        let mut bs = self.consume(0..std::mem::size_of::<i32>())?;
205        Ok(bs.read_i32::<BigEndian>()?)
206    }
207
208    fn consume_i64(&mut self) -> Result<i64> {
209        let mut bs = self.consume(0..std::mem::size_of::<i64>())?;
210        Ok(bs.read_i64::<BigEndian>()?)
211    }
212
213    fn consume_f32(&mut self) -> Result<f32> {
214        let mut bs = self.consume(0..std::mem::size_of::<f32>())?;
215        Ok(bs.read_f32::<BigEndian>()?)
216    }
217
218    fn consume_f64(&mut self) -> Result<f64> {
219        let mut bs = self.consume(0..std::mem::size_of::<f64>())?;
220        Ok(bs.read_f64::<BigEndian>()?)
221    }
222
223    fn ignore_bytes(&mut self, size: usize) -> Result<()> {
224        self.consume(0..size)?;
225        Ok(())
226    }
227}
228
229pub struct Reader<R: Read> {
230    pub(crate) reader: R,
231}
232
233impl<R: Read> private::Sealed for Reader<R> {}
234
235impl<'de, R: Read> Input<'de> for Reader<R> {
236    fn consume_byte(&mut self) -> Result<u8> {
237        Ok(self.reader.read_u8()?)
238    }
239
240    fn ignore_str(&mut self) -> Result<()> {
241        let len = self.reader.read_u16::<BigEndian>()? as usize;
242        let mut buf = vec![0; len]; // TODO: try a scratch space to reduce allocs?
243        Ok(self.reader.read_exact(&mut buf)?)
244    }
245
246    fn consume_str<'s>(&'s mut self, scratch: &'s mut Vec<u8>) -> Result<Reference<'de, 's, str>> {
247        let len = self.reader.read_u16::<BigEndian>()? as usize;
248        scratch.clear();
249        scratch.resize(len, 0);
250        self.reader.read_exact(scratch)?;
251
252        let str = cesu8::from_java_cesu8(scratch).map_err(|_| Error::nonunicode_string(scratch))?;
253
254        Ok(match str {
255            Cow::Borrowed(_) => {
256                Reference::Copied(unsafe { std::str::from_utf8_unchecked(scratch) })
257            }
258            Cow::Owned(s) => {
259                *scratch = s.into_bytes();
260                Reference::Copied(unsafe { std::str::from_utf8_unchecked(scratch) })
261            }
262        })
263    }
264
265    fn consume_bytes<'s>(
266        &'s mut self,
267        n: usize,
268        scratch: &'s mut Vec<u8>,
269    ) -> Result<Reference<'de, 's, [u8]>> {
270        scratch.clear();
271        scratch.resize(n, 0);
272        self.reader.read_exact(scratch.as_mut_slice())?;
273
274        Ok(Reference::Copied(scratch.as_slice()))
275    }
276
277    fn consume_i16(&mut self) -> Result<i16> {
278        Ok(self.reader.read_i16::<BigEndian>()?)
279    }
280
281    fn consume_i32(&mut self) -> Result<i32> {
282        Ok(self.reader.read_i32::<BigEndian>()?)
283    }
284
285    fn consume_i64(&mut self) -> Result<i64> {
286        Ok(self.reader.read_i64::<BigEndian>()?)
287    }
288
289    fn consume_f32(&mut self) -> Result<f32> {
290        Ok(self.reader.read_f32::<BigEndian>()?)
291    }
292
293    fn consume_f64(&mut self) -> Result<f64> {
294        Ok(self.reader.read_f64::<BigEndian>()?)
295    }
296
297    fn ignore_bytes(&mut self, size: usize) -> Result<()> {
298        let mut buf = vec![0; size]; // TODO: try a scratch space to reduce allocs?
299        self.reader.read_exact(&mut buf)?;
300        Ok(())
301    }
302}