1use core::slice;
2
3use crate::endian::Native;
4use crate::error::{CoerceError, CoerceErrorKind, ErrorKind};
5use crate::slice::{BinarySearch, Slice, binary_search_by};
6use crate::stack::Stack;
7use crate::{Buf, Error, Ref, ZeroCopy};
8
9use super::{Flavor, LinksRef, StackEntry, prefix};
10
11pub(super) struct Walk<'a, 'buf, T, F, S>
12where
13 T: ZeroCopy,
14 F: Flavor,
15 S: Stack<StackEntry<'buf, T, F>>,
16{
17 buf: &'buf Buf,
19 state: WalkState<'a, 'buf, T, F>,
21 stack: S,
24}
25
26impl<'a, 'buf, T, F, S> Walk<'a, 'buf, T, F, S>
27where
28 T: ZeroCopy,
29 F: Flavor,
30 S: Stack<StackEntry<'buf, T, F>>,
31{
32 pub(super) fn find(buf: &'buf Buf, links: LinksRef<T, F>, prefix: &'a [u8]) -> Self {
33 Self {
34 buf,
35 state: WalkState::Find(links, prefix),
36 stack: S::new(),
37 }
38 }
39
40 pub(super) fn poll(&mut self) -> Result<Option<(&'buf [u8], &'buf T)>, Error> {
41 'outer: loop {
42 match self.state {
43 WalkState::Find(this, &[]) => {
44 let iter = self.buf.load(this.values)?.iter();
45
46 if !self.stack.try_push((this, 0, &[])) {
47 return Err(Error::new(ErrorKind::StackOverflow {
48 capacity: S::CAPACITY,
49 }));
50 }
51
52 self.state = WalkState::Values(&[], iter);
53 continue;
54 }
55 WalkState::Find(this, string) => {
56 let mut this = this;
57 let mut string = string;
58 let mut len = 0;
59
60 let node = 'node: loop {
61 let search = binary_search_by(self.buf, this.children, |c| {
62 Ok(self.buf.load(c.string)?.cmp(string))
63 })?;
64
65 match search {
66 BinarySearch::Found(n) => {
67 break 'node self.buf.load(this.children.get_unchecked(n))?;
68 }
69 BinarySearch::Missing(n) => {
70 let iter = n
78 .checked_sub(1)
79 .into_iter()
80 .chain((n < this.children.len()).then_some(n));
81
82 for n in iter {
83 let child = self.buf.load(this.children.get_unchecked(n))?;
84
85 let prefix = prefix(self.buf.load(child.string)?, string);
87
88 if prefix == 0 {
89 continue;
90 }
91
92 if prefix != string.len() {
93 len += prefix;
94 string = &string[prefix..];
95 this = child.links;
96 continue 'node;
97 }
98
99 break 'node child;
100 }
101
102 self.state = WalkState::Stack;
107 continue 'outer;
108 }
109 };
110 };
111
112 let prefix = prefix_string(node.string, len)?;
113 let prefix = self.buf.load(prefix)?;
114
115 if !self.stack.try_push((node.links, 0, prefix)) {
116 return Err(Error::new(ErrorKind::StackOverflow {
117 capacity: S::CAPACITY,
118 }));
119 }
120
121 self.state =
122 WalkState::Values(prefix, self.buf.load(node.links.values)?.iter());
123 }
124 WalkState::Values(prefix, ref mut values) => {
125 let Some(value) = values.next() else {
126 self.state = WalkState::Stack;
127 continue;
128 };
129
130 return Ok(Some((prefix, value)));
131 }
132 WalkState::Stack => loop {
133 let Some((links, index, prefix)) = self.stack.pop() else {
134 break 'outer;
135 };
136
137 let Some(node) = links.children.get(index) else {
138 continue;
139 };
140
141 let node = self.buf.load(node)?;
142
143 let new_prefix = prefix_string(node.string, prefix.len())?;
144 let new_prefix = self.buf.load(new_prefix)?;
145
146 self.state =
147 WalkState::Values(new_prefix, self.buf.load(node.links.values)?.iter());
148
149 if !self.stack.try_push((links, index + 1, prefix)) {
150 return Err(Error::new(ErrorKind::StackOverflow {
151 capacity: S::CAPACITY,
152 }));
153 }
154
155 if !self.stack.try_push((node.links, 0, new_prefix)) {
156 return Err(Error::new(ErrorKind::StackOverflow {
157 capacity: S::CAPACITY,
158 }));
159 }
160
161 continue 'outer;
162 },
163 }
164 }
165
166 Ok(None)
167 }
168}
169
170fn prefix_string<S>(string: S, prefix_len: usize) -> Result<Ref<[u8], Native, usize>, CoerceError>
176where
177 S: Slice<Item = u8>,
178{
179 let string_offset = string.offset();
183 let string_len = string.len();
184
185 let Some(real_start) = string_offset.checked_sub(prefix_len) else {
186 return Err(CoerceError::new(CoerceErrorKind::Underflow {
187 at: string_offset,
188 len: prefix_len,
189 }));
190 };
191
192 let Some(real_end) = string_offset.checked_add(string_len) else {
193 return Err(CoerceError::new(CoerceErrorKind::Overflow {
194 at: string_offset,
195 len: string_len,
196 }));
197 };
198
199 Ref::try_with_metadata(real_start, real_end - real_start)
200}
201
202enum WalkState<'a, 'buf, T, F>
203where
204 T: ZeroCopy,
205 F: Flavor,
206{
207 Find(LinksRef<T, F>, &'a [u8]),
209 Values(&'buf [u8], slice::Iter<'buf, T>),
211 Stack,
213}