1use std::io::{Read, Seek};
8use anyhow::Result;
9
10use super::constants::*;
11use super::tag::{read_i32, read_tag_header, TagHeader};
12
13#[derive(Debug, Default, Clone)]
17pub struct Node {
18 pub block: i32,
21 pub entries: Vec<TagHeader>,
23 pub children: Vec<Node>,
25}
26
27impl Node {
28 pub fn find_block(&self, kind: i32) -> Option<&Node> {
30 if self.block == kind {
31 return Some(self);
32 }
33 for child in &self.children {
34 if let Some(found) = child.find_block(kind) {
35 return Some(found);
36 }
37 }
38 None
39 }
40
41 pub fn find_blocks(&self, kind: i32) -> Vec<&Node> {
43 let mut out = Vec::new();
44 self.collect_blocks(kind, &mut out);
45 out
46 }
47
48 fn collect_blocks<'a>(&'a self, kind: i32, out: &mut Vec<&'a Node>) {
49 if self.block == kind {
50 out.push(self);
51 }
52 for child in &self.children {
53 child.collect_blocks(kind, out);
54 }
55 }
56
57 pub fn find_tag(&self, kind: i32) -> Option<&TagHeader> {
60 self.entries.iter().find(|e| e.kind == kind)
61 }
62}
63
64pub fn build_tree(directory: &[TagHeader]) -> Node {
71 let mut stack: Vec<Node> = vec![Node::default()]; for &tag in directory {
73 match tag.kind {
74 FIFF_BLOCK_START => {
75 stack.push(Node::default());
78 }
79 FIFF_BLOCK_END => {
80 let finished = stack.pop().unwrap_or_default();
81 if let Some(parent) = stack.last_mut() {
82 parent.children.push(finished);
83 }
84 }
85 _ => {
86 if let Some(node) = stack.last_mut() {
87 node.entries.push(tag);
88 }
89 }
90 }
91 }
92 while stack.len() > 1 {
94 let orphan = stack.pop().unwrap();
95 stack[0].children.push(orphan);
96 }
97 stack.pop().unwrap_or_default()
98}
99
100pub fn read_tree<R: Read + Seek>(reader: &mut R, directory: &[TagHeader]) -> Result<Node> {
102 let mut root = Node::default();
103 build_tree_resolved(reader, directory, &mut root)?;
104 Ok(root)
105}
106
107fn build_tree_resolved<R: Read + Seek>(
108 reader: &mut R,
109 directory: &[TagHeader],
110 root: &mut Node,
111) -> Result<()> {
112 let mut stack: Vec<Node> = vec![Node {
113 block: 0, entries: Vec::new(),
115 children: Vec::new(),
116 }];
117
118 for &tag in directory {
119 match tag.kind {
120 FIFF_BLOCK_START => {
121 let block_kind = read_i32(reader, &tag).unwrap_or(0);
122 stack.push(Node {
123 block: block_kind,
124 entries: Vec::new(),
125 children: Vec::new(),
126 });
127 }
128 FIFF_BLOCK_END => {
129 let finished = stack.pop().unwrap_or_default();
130 if let Some(parent) = stack.last_mut() {
131 parent.children.push(finished);
132 }
133 }
134 _ => {
135 if let Some(node) = stack.last_mut() {
136 node.entries.push(tag);
137 }
138 }
139 }
140 }
141
142 while stack.len() > 1 {
143 let orphan = stack.pop().unwrap();
144 if let Some(parent) = stack.last_mut() {
145 parent.children.push(orphan);
146 }
147 }
148
149 *root = stack.pop().unwrap_or_default();
150 Ok(())
151}
152
153pub fn scan_directory<R: Read + Seek>(reader: &mut R) -> Result<Vec<TagHeader>> {
159 let mut directory = Vec::new();
160 let mut pos: Option<u64> = Some(0);
161 while let Some(p) = pos {
162 let tag = read_tag_header(reader, p)?;
163 pos = tag.next_pos();
164 directory.push(tag);
165 }
166 Ok(directory)
167}
168
169pub fn try_load_directory<R: Read + Seek>(reader: &mut R) -> Result<Option<Vec<TagHeader>>> {
177 let id_tag = read_tag_header(reader, 0)?;
179 if id_tag.kind != FIFF_FILE_ID {
180 return Ok(None);
181 }
182 let next = match id_tag.next_pos() {
184 Some(p) => p,
185 None => return Ok(None),
186 };
187 let dir_ptr_tag = read_tag_header(reader, next)?;
188 if dir_ptr_tag.kind != FIFF_DIR_POINTER {
189 return Ok(None);
190 }
191 let dirpos = read_i32(reader, &dir_ptr_tag)? as i64;
192 if dirpos <= 0 {
193 return Ok(None);
194 }
195 let dir_tag = read_tag_header(reader, dirpos as u64)?;
196 if dir_tag.ftype != super::constants::FIFFT_DIR_ENTRY_STRUCT {
197 return Ok(None);
198 }
199 let entries = super::tag::read_directory(reader, &dir_tag)?;
200 Ok(Some(entries))
201}
202
203#[cfg(test)]
204mod tests {
205 use super::*;
206
207 fn make_flat_dir(spec: &[(i32, u32, i32, i32)]) -> Vec<TagHeader> {
208 spec.iter()
209 .enumerate()
210 .map(|(i, &(kind, ftype, size, next))| TagHeader {
211 kind, ftype, size, next, pos: (i as u64) * 100,
212 })
213 .collect()
214 }
215
216 #[test]
217 fn flat_directory_no_blocks() {
218 let dir = make_flat_dir(&[
220 (FIFF_NCHAN, FIFFT_INT, 4, 0),
221 (FIFF_SFREQ, FIFFT_FLOAT, 4, 0),
222 ]);
223 let root = build_tree(&dir);
224 assert_eq!(root.entries.len(), 2);
225 assert!(root.children.is_empty());
226 }
227
228 #[test]
229 fn single_block() {
230 let dir = make_flat_dir(&[
231 (FIFF_BLOCK_START, FIFFT_INT, 4, 0), (FIFF_NCHAN, FIFFT_INT, 4, 0),
233 (FIFF_BLOCK_END, FIFFT_INT, 4, 0),
234 ]);
235 let root = build_tree(&dir);
236 assert_eq!(root.children.len(), 1);
237 assert_eq!(root.children[0].entries.len(), 1);
238 }
239
240 #[test]
241 fn nested_blocks() {
242 let dir = make_flat_dir(&[
243 (FIFF_BLOCK_START, FIFFT_INT, 4, 0), (FIFF_BLOCK_START, FIFFT_INT, 4, 0), (FIFF_NCHAN, FIFFT_INT, 4, 0),
246 (FIFF_BLOCK_END, FIFFT_INT, 4, 0), (FIFF_SFREQ, FIFFT_FLOAT, 4, 0),
248 (FIFF_BLOCK_END, FIFFT_INT, 4, 0), ]);
250 let root = build_tree(&dir);
251 assert_eq!(root.children.len(), 1); let outer = &root.children[0];
253 assert_eq!(outer.children.len(), 1); assert_eq!(outer.entries.len(), 1); assert_eq!(outer.children[0].entries.len(), 1); }
257
258 #[test]
259 fn find_block_depth_first() {
260 let dir = make_flat_dir(&[
261 (FIFF_BLOCK_START, FIFFT_INT, 4, 0),
262 (FIFF_BLOCK_START, FIFFT_INT, 4, 0),
263 (FIFF_NCHAN, FIFFT_INT, 4, 0),
264 (FIFF_BLOCK_END, FIFFT_INT, 4, 0),
265 (FIFF_BLOCK_END, FIFFT_INT, 4, 0),
266 ]);
267 let root = build_tree(&dir);
268 assert!(root.find_block(0).is_some());
271 }
272}