snops_common/format/
impl_strings.rs1use std::io::{Read, Write};
2
3use lasso::Spur;
4
5use super::{packed_int::PackedUint, DataFormat, DataReadError, DataWriteError};
6use crate::INTERN;
7
8impl DataFormat for String {
9 type Header = ();
10 const LATEST_HEADER: Self::Header = ();
11
12 fn write_data<W: Write>(&self, writer: &mut W) -> Result<usize, DataWriteError> {
13 let bytes = self.as_bytes();
14 Ok(PackedUint::from(bytes.len()).write_data(writer)? + writer.write(bytes)?)
15 }
16
17 fn read_data<R: Read>(reader: &mut R, _header: &Self::Header) -> Result<Self, DataReadError> {
18 let len = usize::from(PackedUint::read_data(reader, &())?);
19 let mut buf = String::with_capacity(len);
20 let read_len = reader.take(len as u64).read_to_string(&mut buf)?;
21 if read_len != len {
22 return Err(DataReadError::Custom(format!(
23 "string expected to read {} bytes, but read {}",
24 len, read_len
25 )));
26 }
27 Ok(buf)
28 }
29}
30
31impl DataFormat for Spur {
32 type Header = ();
33 const LATEST_HEADER: Self::Header = ();
34
35 fn write_data<W: std::io::prelude::Write>(
36 &self,
37 writer: &mut W,
38 ) -> Result<usize, crate::format::DataWriteError> {
39 let s: &str = INTERN.resolve(self);
40 let bytes = s.as_bytes();
41 Ok(PackedUint::from(bytes.len()).write_data(writer)? + writer.write(bytes)?)
42 }
43
44 fn read_data<R: std::io::prelude::Read>(
45 reader: &mut R,
46 _header: &Self::Header,
47 ) -> Result<Self, crate::format::DataReadError> {
48 let data = String::read_data(reader, &())?;
49 Ok(INTERN.get_or_intern(data))
50 }
51}
52
53#[cfg(test)]
54#[rustfmt::skip]
55mod test {
56 use lasso::Spur;
57
58 use crate::{format::DataFormat, INTERN};
59
60 macro_rules! case {
61 ($name:ident, $ty:ty, $a:expr, $b:expr) => {
62 #[test]
63 fn $name() {
64 let mut data = Vec::new();
65 $a.write_data(&mut data).unwrap();
66 assert_eq!(data, $b);
67
68 let mut reader = &data[..];
69 let read_value = <$ty>::read_data(&mut reader, &()).unwrap();
70 assert_eq!(read_value, $a);
71
72 }
73
74 };
75 }
76
77 case!(test_string, String, "hello".to_string(), b"\x01\x05hello");
78 case!(test_spur, Spur, INTERN.get_or_intern("hello"), b"\x01\x05hello");
79 case!(test_long_string, String, "This is a long string".to_string(), b"\x01\x15This is a long string");
81 case!(test_interned_string, Spur, INTERN.get_or_intern("This is an interned string"), b"\x01\x1AThis is an interned string");
83 case!(test_empty_string, String, "".to_string(), [0]);
84 case!(test_empty_spur, Spur, INTERN.get_or_intern(""), [0]);
85}