geonative_shapefile/
bytes.rs1use crate::error::{Result, ShpError};
11
12#[derive(Debug, Clone)]
13pub struct Cursor<'a> {
14 bytes: &'a [u8],
15 pos: usize,
16}
17
18impl<'a> Cursor<'a> {
19 pub fn new(bytes: &'a [u8]) -> Self {
20 Self { bytes, pos: 0 }
21 }
22
23 pub fn position(&self) -> usize {
24 self.pos
25 }
26
27 pub fn remaining(&self) -> usize {
28 self.bytes.len() - self.pos
29 }
30
31 pub fn seek(&mut self, pos: usize) -> Result<()> {
32 if pos > self.bytes.len() {
33 return Err(ShpError::malformed(format!(
34 "seek past EOF: {pos} > {}",
35 self.bytes.len()
36 )));
37 }
38 self.pos = pos;
39 Ok(())
40 }
41
42 fn need(&self, n: usize) -> Result<()> {
43 if self.remaining() < n {
44 Err(ShpError::malformed(format!(
45 "truncated input at byte {}: need {} more, have {}",
46 self.pos,
47 n,
48 self.remaining()
49 )))
50 } else {
51 Ok(())
52 }
53 }
54
55 pub fn read_bytes(&mut self, n: usize) -> Result<&'a [u8]> {
56 self.need(n)?;
57 let s = &self.bytes[self.pos..self.pos + n];
58 self.pos += n;
59 Ok(s)
60 }
61
62 pub fn read_i32_be(&mut self) -> Result<i32> {
63 let s = self.read_bytes(4)?;
64 Ok(i32::from_be_bytes(s.try_into().unwrap()))
65 }
66
67 pub fn read_i32_le(&mut self) -> Result<i32> {
68 let s = self.read_bytes(4)?;
69 Ok(i32::from_le_bytes(s.try_into().unwrap()))
70 }
71
72 pub fn read_f64_le(&mut self) -> Result<f64> {
73 let s = self.read_bytes(8)?;
74 Ok(f64::from_le_bytes(s.try_into().unwrap()))
75 }
76}
77
78#[inline]
80pub fn words_to_bytes(words: i32) -> usize {
81 (words as i64 * 2) as usize
82}
83
84#[cfg(test)]
85mod tests {
86 use super::*;
87
88 #[test]
89 fn reads_mixed_endianness() {
90 let mut c = Cursor::new(&[0, 0, 0, 1, 1, 0, 0, 0]);
92 assert_eq!(c.read_i32_be().unwrap(), 1);
93 assert_eq!(c.read_i32_le().unwrap(), 1);
94 }
95
96 #[test]
97 fn truncation_errors() {
98 let mut c = Cursor::new(&[1, 2, 3]);
99 assert!(c.read_i32_be().is_err());
100 }
101
102 #[test]
103 fn words_conversion() {
104 assert_eq!(words_to_bytes(50), 100);
105 }
106}