1use std::collections::HashMap;
2use std::num::NonZeroU64;
3
4#[repr(transparent)]
5#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
6pub struct Inode(NonZeroU64);
7
8impl Inode {
9 pub fn new(value: u64) -> std::io::Result<Inode> {
10 match NonZeroU64::new(value) {
11 Some(v) => Ok(Inode(v)),
12 None => Err(std::io::Error::new(
13 std::io::ErrorKind::InvalidInput,
14 "inode must not be zero",
15 )),
16 }
17 }
18
19 pub fn get(self) -> u64 {
20 self.0.get()
21 }
22}
23
24pub(crate) mod meta;
25#[cfg(feature = "reader")]
26pub mod reader;
27#[cfg(feature = "writer")]
28pub mod writer;
29
30pub use self::meta::BoxMetadata;
31
32pub type AttrMap = HashMap<usize, Vec<u8>>;
33
34#[cfg(test)]
35mod tests {
36 use crate::{compression::Compression, *};
37 use std::collections::HashMap;
38 use std::io::prelude::*;
39 use std::io::Cursor;
40 use std::path::Path;
41
42 fn create_test_box<F: AsRef<Path>>(filename: F) {
43 let _ = std::fs::remove_file(filename.as_ref());
44
45 let mut cursor: Cursor<Vec<u8>> = Cursor::new(vec![]);
46 let data = b"hello\0\0\0";
47 cursor.write_all(data).unwrap();
48 cursor.seek(std::io::SeekFrom::Start(0)).unwrap();
49
50 let mut writer = BoxFileWriter::create(filename).unwrap();
51 writer
52 .insert(
53 Compression::Stored,
54 BoxPath::new("hello.txt").unwrap(),
55 &mut cursor,
56 HashMap::new(),
57 )
58 .unwrap();
59 }
60
61 #[test]
62 fn create_box_file() {
63 create_test_box("./smoketest.box");
64 }
65
66 #[test]
67 fn read_garbage() {
68 let filename = "./read_garbage.box";
69 create_test_box(filename);
70
71 let bf = BoxFileReader::open(filename).unwrap();
72 let trailer = bf.metadata();
73 println!("{:?}", bf.header);
74 println!("{:?}", &trailer);
75 let file_data = unsafe { bf.memory_map(trailer.inodes[0].as_file().unwrap()).unwrap() };
76 println!("{:?}", &*file_data);
77 assert_eq!(&*file_data, b"hello\0\0\0")
78 }
79
80 #[test]
81 fn create_garbage() {
82 let filename = "./create_garbage.box";
83 let _ = std::fs::remove_file(filename);
84 let bf = BoxFileWriter::create(filename).expect("Mah box");
85 bf.finish().unwrap();
86 }
87
88 #[test]
89 fn read_bytes() {
90 let filename = "./read_bytes.box";
91 create_test_box(filename);
92 let bf = BoxFileReader::open(filename).unwrap();
93 let record = bf
94 .metadata()
95 .inodes
96 .first()
97 .map(|f| f.as_file().unwrap())
98 .unwrap();
99 let mut reader = bf.read_bytes(record).unwrap();
100 let mut vec = vec![];
101 reader.read_to_end(&mut vec).unwrap();
102 assert_eq!(vec, b"hello\0\0\0")
103 }
104
105 fn insert_impl<F>(filename: &str, f: F)
106 where
107 F: Fn(&str) -> BoxFileWriter,
108 {
109 let _ = std::fs::remove_file(filename);
110 let v =
111 "This, this, this, this, this is a compressable string string string string string.\n"
112 .to_string();
113
114 {
115 use std::time::SystemTime;
116 let now = SystemTime::now()
117 .duration_since(SystemTime::UNIX_EPOCH)
118 .unwrap()
119 .as_secs()
120 .to_le_bytes();
121
122 let mut bf = f(filename);
123
124 let mut dir_attrs = HashMap::new();
125 dir_attrs.insert("created".into(), now.to_vec());
126 dir_attrs.insert("unix.mode".into(), 0o755u16.to_le_bytes().to_vec());
127
128 let mut attrs = HashMap::new();
129 attrs.insert("created".into(), now.to_vec());
130 attrs.insert("unix.mode".into(), 0o644u16.to_le_bytes().to_vec());
131
132 bf.mkdir(BoxPath::new("test").unwrap(), dir_attrs).unwrap();
133
134 bf.insert(
135 Compression::Zstd,
136 BoxPath::new("test/string.txt").unwrap(),
137 &mut std::io::Cursor::new(v.clone()),
138 attrs.clone(),
139 )
140 .unwrap();
141 bf.insert(
142 Compression::Deflate,
143 BoxPath::new("test/string2.txt").unwrap(),
144 &mut std::io::Cursor::new(v.clone()),
145 attrs.clone(),
146 )
147 .unwrap();
148 bf.finish().unwrap();
150 }
151
152 let bf = BoxFileReader::open(filename).expect("Mah box");
153 println!("{:#?}", &bf);
154
155 assert_eq!(
156 v,
157 bf.decompress_value::<String>(bf.meta.inodes[1].as_file().unwrap())
158 .unwrap()
159 );
160 assert_eq!(
161 v,
162 bf.decompress_value::<String>(bf.meta.inodes[2].as_file().unwrap())
163 .unwrap()
164 );
165 }
166
167 #[test]
168 fn insert() {
169 insert_impl("./insert_garbage.box", |n| {
170 BoxFileWriter::create(n).unwrap()
171 });
172 insert_impl("./insert_garbage_align8.box", |n| {
173 BoxFileWriter::create_with_alignment(n, 8).unwrap()
174 });
175 insert_impl("./insert_garbage_align7.box", |n| {
176 BoxFileWriter::create_with_alignment(n, 7).unwrap()
177 });
178 }
179
180 }