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