1#[cfg(all(feature = "_bench", test))]
2mod bench;
3#[cfg(test)]
4mod tests;
5
6use crate::{
7 collection::{ArrayVectorU8, ShortBoxStrU8, ShortStrU8, Vector},
8 http::{DEFAULT_MAX_CHILDREN, DEFAULT_MAX_DEPTH},
9 misc::{bytes_pos1, from_utf8_basic},
10};
11use core::{
12 hint::{cold_path, unreachable_unchecked},
13 mem,
14 range::Range,
15 str,
16};
17
18#[derive(Clone, Copy, Debug)]
20pub enum MatcherError {
21 AddErrDuplicate,
23 AddErrEmpty,
25 AddErrInvalidStart,
27 AddErrMultipleRouteParameters,
29 AddErrNonAsciiLiteral,
31 AddErrNonAsciiParam,
33 AddErrOverflow,
35 AddErrParamSuffix,
37 AddErrUnclosedParameterBracket,
39 FindErrEmpty,
42 FindErrMismatch,
44 FindErrOverflow,
46}
47
48#[derive(Clone, Debug)]
53pub struct Matcher<T, const MC: usize = DEFAULT_MAX_CHILDREN, const MD: usize = DEFAULT_MAX_DEPTH> {
54 rows: Vector<Row<T, MC>>,
55}
56
57impl<T, const MC: usize, const MD: usize> Matcher<T, MC, MD> {
58 #[inline]
60 pub const fn new() -> Self {
61 Self { rows: Vector::new() }
62 }
63}
64
65impl<T, const MC: usize, const MD: usize> Matcher<T, MC, MD> {
66 #[inline]
68 pub fn builder(&mut self) -> MatcherBuilder<'_, T, MC, MD> {
69 self.rows.clear();
70 MatcherBuilder { rows: &mut self.rows }
71 }
72
73 #[inline]
75 pub fn find<'this, 'data, 'rslt>(
76 &'this self,
77 route_str: &'data str,
78 ) -> crate::Result<MatcherPath<'rslt, T, MC, MD>>
79 where
80 'data: 'rslt,
81 'this: 'rslt,
82 {
83 let Self { rows } = self;
84 let route_short_str = ShortStrU8::new(route_str)?;
85 let route = route_short_str.into_str().as_bytes();
86 let route_len = route_short_str.len();
87 let curr_route = &mut route_short_str.into_str().as_bytes();
88 let mut edges;
89 let mut path_rows = ArrayVectorU8::new();
90
91 let Some(first_row) = rows.first() else {
92 cold_path();
93 return Err(MatcherError::FindErrEmpty.into());
94 };
95 match Self::check_row(curr_route, &mut path_rows, route_len, first_row, 0) {
96 CheckSearchRowRslt::CompleteMatch(value) => {
97 return Ok(MatcherPath { path_rows, route, rows, value });
98 }
99 CheckSearchRowRslt::IncompleteMatch(local_edges) => edges = local_edges,
100 CheckSearchRowRslt::Mismatch => return Err(MatcherError::FindErrMismatch.into()),
101 }
102
103 loop {
104 let [statics @ .., last] = edges.as_slice() else {
105 return Err(MatcherError::FindErrMismatch.into());
106 };
107 let curr_ident_first = curr_route.first().copied();
108 let last_edge_has_param = last.first_byte.is_none();
109 if last_edge_has_param {
110 if let Some(edge) = statics.iter().find(|el| el.first_byte == curr_ident_first) {
111 match Self::check_edge(*edge, &mut edges, curr_route, &mut path_rows, route_len, rows) {
112 CheckSearchRowRslt::CompleteMatch(value) => {
113 return Ok(MatcherPath { path_rows, route, rows, value });
114 }
115 CheckSearchRowRslt::IncompleteMatch(_) => continue,
116 CheckSearchRowRslt::Mismatch => {}
117 }
118 }
119 match Self::check_edge(*last, &mut edges, curr_route, &mut path_rows, route_len, rows) {
120 CheckSearchRowRslt::CompleteMatch(value) => {
121 return Ok(MatcherPath { path_rows, route, rows, value });
122 }
123 CheckSearchRowRslt::IncompleteMatch(_) => {}
124 CheckSearchRowRslt::Mismatch => cold_path(),
125 }
126 } else {
127 let Some(edge) = edges.iter().find(|el| el.first_byte == curr_ident_first) else {
128 return Err(MatcherError::FindErrMismatch.into());
129 };
130 match Self::check_edge(*edge, &mut edges, curr_route, &mut path_rows, route_len, rows) {
131 CheckSearchRowRslt::CompleteMatch(value) => {
132 return Ok(MatcherPath { path_rows, route, rows, value });
133 }
134 CheckSearchRowRslt::IncompleteMatch(_) => {}
135 CheckSearchRowRslt::Mismatch => return Err(MatcherError::FindErrMismatch.into()),
136 }
137 }
138 }
139 }
140
141 #[inline(always)]
142 fn check_edge<'this>(
143 edge: Edge,
144 edges: &mut &'this ArrayVectorU8<Edge, MC>,
145 curr_route: &mut &'this [u8],
146 path_rows: &mut ArrayVectorU8<PathRow, MD>,
147 route_len: u8,
148 rows: &'this [Row<T, MC>],
149 ) -> CheckSearchRowRslt<'this, T, MC> {
150 let row_idx = edge.row_target_idx;
151 let row = unsafe { rows.get(usize::from(row_idx)).unwrap_unchecked() };
153 match Self::check_row(curr_route, path_rows, route_len, row, row_idx) {
154 CheckSearchRowRslt::CompleteMatch(value) => CheckSearchRowRslt::CompleteMatch(value),
155 CheckSearchRowRslt::IncompleteMatch(local_edges) => {
156 *edges = local_edges;
157 CheckSearchRowRslt::IncompleteMatch(local_edges)
158 }
159 CheckSearchRowRslt::Mismatch => CheckSearchRowRslt::Mismatch,
160 }
161 }
162
163 #[inline(always)]
164 fn check_row<'this>(
165 curr_route: &mut &'this [u8],
166 path_rows: &mut ArrayVectorU8<PathRow, MD>,
167 route_len: u8,
168 row: &'this Row<T, MC>,
169 row_idx: u8,
170 ) -> CheckSearchRowRslt<'this, T, MC> {
171 match row.ty {
172 RowTy::Literal => {
173 if let Some(rest) = curr_route.strip_prefix(row.route.as_bytes()) {
174 *curr_route = rest;
175 } else {
176 return CheckSearchRowRslt::Mismatch;
177 }
178 }
179 #[expect(
180 clippy::as_conversions,
181 clippy::cast_possible_truncation,
182 reason = "full routes are limited by 255, subroutes are smaller"
183 )]
184 RowTy::Param => {
185 let (param, rest) = if let Some(param_end_idx) = bytes_pos1(*curr_route, b'/') {
187 unsafe { curr_route.split_at_checked(param_end_idx).unwrap_unchecked() }
189 } else {
190 (*curr_route, &[][..])
191 };
192 let begin_idx = route_len.wrapping_sub(curr_route.len() as u8);
193 let end_idx = begin_idx.wrapping_add(param.len() as u8);
194 unsafe {
197 path_rows.push(PathRow::new((begin_idx..end_idx).into(), row_idx)).unwrap_unchecked();
198 }
199 *curr_route = rest;
200 }
201 }
202 if let ([], Some(value)) = (curr_route, &row.value) {
203 return CheckSearchRowRslt::CompleteMatch(value);
204 }
205 CheckSearchRowRslt::IncompleteMatch(&row.edges)
206 }
207}
208
209impl<T> Default for Matcher<T> {
210 #[inline]
211 fn default() -> Self {
212 Self::new()
213 }
214}
215
216#[derive(Debug)]
218pub struct MatcherBuilder<
219 'instance,
220 T,
221 const MC: usize = DEFAULT_MAX_CHILDREN,
222 const MD: usize = DEFAULT_MAX_DEPTH,
223> {
224 rows: &'instance mut Vector<Row<T, MC>>,
225}
226
227impl<T, const MC: usize, const MD: usize> MatcherBuilder<'_, T, MC, MD> {
228 #[inline]
230 pub fn add(&mut self, route: &ShortBoxStrU8, value: T) -> crate::Result<&mut Self> {
231 let mut curr_bytes_idx = 0;
232 loop {
233 let (mut local_route, local_ty) = Self::manage_row_ty(route.as_bytes(), &mut curr_bytes_idx)?;
234 let final_row_idx = Self::add_local_route(&mut local_route, local_ty, self.rows)?;
235 let should_stop = curr_bytes_idx >= usize::from(route.len());
236 if should_stop {
237 if let Some(row) = self.rows.get_mut(usize::from(final_row_idx)) {
238 if row.value.is_some() {
239 return Err(MatcherError::AddErrDuplicate.into());
240 }
241 row.value = Some(value);
242 }
243 return Ok(self);
244 }
245 }
246 }
247
248 #[inline]
249 fn add_local_route(
250 local_route: &mut &[u8],
251 local_ty: RowTy,
252 rows: &mut Vector<Row<T, MC>>,
253 ) -> crate::Result<u8> {
254 let mut row_idx = 0;
255
256 let mut edges = if let Some(row) = rows.first() {
257 let crr = Self::compare_row(local_route, row)?;
258 match crr {
259 CompareRowRslt::Finished => {
260 return Ok(row_idx);
261 }
262 CompareRowRslt::RowFull(edges) => edges,
263 CompareRowRslt::RowPartial { common_prefix_len, is_single } => {
264 Self::add_row((local_route, local_ty), common_prefix_len, is_single, &mut row_idx, rows)?;
265 return Ok(row_idx);
266 }
267 CompareRowRslt::Unmatched => return Ok(row_idx),
269 }
270 } else {
271 let row_route = from_utf8_basic(local_route)?.try_into()?;
272 rows.push(Row::new(ArrayVectorU8::new(), row_route, local_ty, None))?;
273 return Ok(row_idx);
274 };
275
276 'outer: loop {
277 'inner: for edge in edges {
278 let Some(row) = rows.get_mut(usize::from(edge.row_target_idx)) else {
279 break 'inner;
280 };
281 let crr = Self::compare_row(local_route, row)?;
282 match crr {
283 CompareRowRslt::Finished => {
284 return Ok(edge.row_target_idx);
285 }
286 CompareRowRslt::RowFull(local_edges) => {
287 row_idx = edge.row_target_idx;
288 edges = local_edges;
289 continue 'outer;
290 }
291 CompareRowRslt::RowPartial { common_prefix_len, is_single } => {
292 row_idx = edge.row_target_idx;
293 Self::add_row(
294 (local_route, local_ty),
295 common_prefix_len,
296 is_single,
297 &mut row_idx,
298 rows,
299 )?;
300 return Ok(row_idx);
301 }
302 CompareRowRslt::Unmatched => {}
303 }
304 }
305 Self::add_row((local_route, local_ty), 0, true, &mut row_idx, rows)?;
306 return Ok(row_idx);
307 }
308 }
309
310 #[inline]
311 fn add_row(
312 (route, ty): (&[u8], RowTy),
313 common_prefix_len: usize,
314 is_single: bool,
315 row_idx: &mut u8,
316 rows: &mut Vector<Row<T, MC>>,
317 ) -> crate::Result<()> {
318 let route_str: ShortBoxStrU8 = route.try_into()?;
319 let parent_idx = usize::from(*row_idx);
320 if is_single {
321 let child_idx = u8::try_from(rows.len()).map_err(|_err| MatcherError::AddErrOverflow)?;
322 if route_str.is_empty() {
323 let Some(parent) = rows.get_mut(parent_idx) else {
324 return Ok(());
325 };
326 let child_edges = mem::take(&mut parent.edges);
327 let child_route = unique_route(common_prefix_len, &parent.route)?;
328 let child_value = parent.value.take();
329 parent.edges.push(Edge::new(None, child_idx))?;
330 parent.route = common_route(common_prefix_len, &parent.route)?;
331 parent.ty = ty;
332 rows.push(Row::new(child_edges, child_route, ty, child_value))?;
333 } else {
334 let Some(parent) = rows.get_mut(parent_idx) else {
335 return Ok(());
336 };
337 parent.edges.push(Edge::new(None, child_idx))?;
338 rows.push(Row::new(ArrayVectorU8::new(), route_str, ty, None))?;
339 *row_idx = child_idx;
340 }
341 } else {
342 let child0_idx = u8::try_from(rows.len()).map_err(|_err| MatcherError::AddErrOverflow)?;
343 let child1_idx = child0_idx.checked_add(1).ok_or(MatcherError::AddErrOverflow)?;
344 let Some(parent) = rows.get_mut(parent_idx) else {
345 return Ok(());
346 };
347 let child0_edges = mem::take(&mut parent.edges);
348 let child0_route = unique_route(common_prefix_len, &parent.route)?;
349 let child0_value = parent.value.take();
350 parent.edges.push(Edge::new(None, child0_idx))?;
351 parent.edges.push(Edge::new(None, child1_idx))?;
352 parent.route = common_route(common_prefix_len, &parent.route)?;
353 rows.push(Row::new(child0_edges, child0_route, RowTy::Literal, child0_value))?;
354 rows.push(Row::new(ArrayVectorU8::new(), route_str, ty, None))?;
355 *row_idx = child1_idx;
356 }
357
358 Ok(())
359 }
360
361 #[inline]
362 fn common_prefix_len(lhs: &[u8], rhs: &[u8]) -> usize {
363 lhs.iter().zip(rhs).take_while(|(b0, b1)| b0 == b1).count()
364 }
365
366 #[inline]
367 fn compare_row(route: &mut &[u8], row: &Row<T, MC>) -> crate::Result<CompareRowRslt<MC>> {
368 if row.ty == RowTy::Param {
369 if route.first() == Some(&b'{')
370 && let Some(idx) = bytes_pos1(*route, b'}')
371 && let Some((lhs, rhs)) = route.split_at_checked(idx.wrapping_add(1))
372 {
373 if lhs == row.route.as_bytes() {
374 *route = rhs;
375 if rhs.is_empty() {
376 return Ok(CompareRowRslt::Finished);
377 }
378 return Ok(CompareRowRslt::RowFull(row.edges.clone()));
379 }
380 return Err(MatcherError::AddErrMultipleRouteParameters.into());
381 }
382 return Ok(CompareRowRslt::Unmatched);
383 }
384
385 let row_route = row.route.as_bytes();
386 let common_prefix_len = Self::common_prefix_len(route, row_route);
387 let (_lhs, rhs) = route.split_at_checked(common_prefix_len).unwrap_or_default();
388 let is_row_fulfilled = common_prefix_len == row_route.len();
389 if common_prefix_len == 0 {
390 Ok(CompareRowRslt::Unmatched)
391 } else if is_row_fulfilled {
392 *route = rhs;
393 if rhs.is_empty() {
394 return Ok(CompareRowRslt::Finished);
395 }
396 Ok(CompareRowRslt::RowFull(row.edges.clone()))
397 } else {
398 *route = rhs;
399 Ok(CompareRowRslt::RowPartial { common_prefix_len, is_single: rhs.is_empty() })
400 }
401 }
402
403 #[inline]
405 fn manage_row_ty<'bytes>(
406 bytes: &'bytes [u8],
407 curr_bytes_idx: &mut usize,
408 ) -> crate::Result<(&'bytes [u8], RowTy)> {
409 let curr_bytes = bytes.get(*curr_bytes_idx..).unwrap_or_default();
410 let Some(([first], rest)) = curr_bytes.split_at_checked(1) else {
411 return Err(MatcherError::AddErrEmpty.into());
412 };
413 let mut iter = rest.iter();
414 let mut route_end_idx: u8 = 1;
415 match first {
416 b'{' => {
417 for byte in iter.by_ref() {
418 match byte {
419 b'{' => return Err(MatcherError::AddErrMultipleRouteParameters.into()),
420 b'}' => {
421 let next = iter.next().copied();
422 if next.is_some() && next != Some(b'/') {
423 return Err(MatcherError::AddErrParamSuffix.into());
424 }
425 route_end_idx = route_end_idx.checked_add(1).ok_or(MatcherError::AddErrOverflow)?;
426 *curr_bytes_idx = curr_bytes_idx.wrapping_add(route_end_idx.into());
427 let route = bytes.get(..*curr_bytes_idx).unwrap_or_default();
428 return Ok((route, RowTy::Param));
429 }
430 other => {
431 if !other.is_ascii_graphic() {
432 return Err(MatcherError::AddErrNonAsciiParam.into());
433 }
434 }
435 }
436 route_end_idx = route_end_idx.checked_add(1).ok_or(MatcherError::AddErrOverflow)?;
437 }
438 Err(MatcherError::AddErrUnclosedParameterBracket.into())
439 }
440 b'/' => {
441 for byte in iter {
442 match byte {
443 b'{' => break,
444 other => {
445 if !other.is_ascii_graphic() {
446 return Err(MatcherError::AddErrNonAsciiLiteral.into());
447 }
448 }
449 }
450 route_end_idx = route_end_idx.checked_add(1).ok_or(MatcherError::AddErrOverflow)?;
451 }
452 *curr_bytes_idx = curr_bytes_idx.wrapping_add(route_end_idx.into());
453 let route = bytes.get(..*curr_bytes_idx).unwrap_or_default();
454 Ok((route, RowTy::Literal))
455 }
456 _ => Err(MatcherError::AddErrInvalidStart.into()),
457 }
458 }
459}
460
461impl<T, const MC: usize, const MD: usize> Drop for MatcherBuilder<'_, T, MC, MD> {
462 #[inline]
463 fn drop(&mut self) {
464 #[inline]
465 fn evaluate_max_depth<T, const MC: usize, const MD: usize>(rows: &mut Vector<Row<T, MC>>) {
466 let mut depths = alloc::vec![0u8; rows.len()];
467 let mut max_depth = 0;
468 for (idx, row) in rows.iter().enumerate() {
469 let next_depth = {
470 let Some(curr_depth) = depths.get(idx) else {
471 continue;
472 };
473 if row.ty == RowTy::Param { curr_depth.saturating_add(1) } else { *curr_depth }
474 };
475 if next_depth > max_depth {
476 max_depth = next_depth;
477 }
478 for edge in &row.edges {
479 let target = usize::from(edge.row_target_idx);
480 if target >= depths.len() {
481 continue;
482 }
483 let Some(depth) = depths.get_mut(target) else {
484 continue;
485 };
486 *depth = next_depth;
487 }
488 }
489 if usize::from(max_depth) > MD {
490 rows.clear();
491 }
492 }
493
494 #[inline]
495 fn fill_first_bytes<T, const MC: usize>(rows: &mut Vector<Row<T, MC>>) {
496 let mut row_idx: usize = 0;
497 while row_idx < rows.len() {
498 let mut first_bytes = ArrayVectorU8::<_, MC>::new();
499 if let Some(row) = rows.get(row_idx) {
500 for edge in &row.edges {
501 let Some(target_row) = rows.get(usize::from(edge.row_target_idx)) else {
502 continue;
503 };
504 let first = match target_row.ty {
505 RowTy::Literal => target_row.route.as_bytes().first().copied(),
506 RowTy::Param => None,
507 };
508 let _rslt = first_bytes.push(first);
509 }
510 }
511 if let Some(row) = rows.get_mut(row_idx) {
512 for (edge, first_byte) in row.edges.iter_mut().zip(first_bytes) {
513 edge.first_byte = first_byte;
514 }
515 }
516 row_idx = row_idx.wrapping_add(1);
517 }
518 for row in rows {
519 let Some(param_idx) = row.edges.iter().position(|el| el.first_byte.is_none()) else {
520 continue;
521 };
522 let last_idx = row.edges.len().wrapping_sub(1).into();
523 row.edges.swap(param_idx, last_idx);
524 }
525 }
526
527 #[inline]
528 fn sort_by_the_number_of_children<T, const MC: usize>(rows: &mut Vector<Row<T, MC>>) {
529 let mut weights = alloc::vec![0u32; rows.len()];
530 for idx in (0..rows.len()).rev() {
531 let Some(row) = rows.get(idx) else {
532 continue;
533 };
534 let mut weight = u32::from(row.value.is_some());
535 for edge in &row.edges {
536 let weight_node = weights.get(usize::from(edge.row_target_idx)).copied();
537 weight = weight.wrapping_add(weight_node.unwrap_or_default());
538 }
539 if let Some(elem) = weights.get_mut(idx) {
540 *elem = weight;
541 }
542 }
543 for row in rows {
544 row.edges.sort_unstable_by(|lhs, rhs| {
545 let weight_a = weights.get(usize::from(lhs.row_target_idx)).copied().unwrap_or_default();
546 let weight_b = weights.get(usize::from(rhs.row_target_idx)).copied().unwrap_or_default();
547 weight_b.cmp(&weight_a)
548 });
549 }
550 }
551
552 sort_by_the_number_of_children(self.rows);
553 fill_first_bytes(self.rows);
554 evaluate_max_depth::<_, _, MD>(self.rows);
555 }
556}
557
558#[derive(Debug, PartialEq)]
560pub struct MatcherPath<
561 'any,
562 T,
563 const MC: usize = DEFAULT_MAX_CHILDREN,
564 const MD: usize = DEFAULT_MAX_DEPTH,
565> {
566 path_rows: ArrayVectorU8<PathRow, MD>,
567 route: &'any [u8],
568 rows: &'any Vector<Row<T, MC>>,
569 value: &'any T,
570}
571
572impl<T, const MC: usize, const MD: usize> MatcherPath<'_, T, MC, MD> {
573 #[inline]
575 pub fn data(&self) -> &T {
576 self.value
577 }
578
579 #[inline]
581 pub fn param_by_idx(&self, idx: usize) -> Option<MatcherPathParam<'_>> {
582 self.params().nth(idx)
583 }
584
585 #[inline]
587 pub fn param_by_name(&self, name: &[u8]) -> Option<MatcherPathParam<'_>> {
588 self.params().find(|el| el.name.as_bytes() == name)
589 }
590
591 #[inline]
593 pub fn params(&self) -> impl Iterator<Item = MatcherPathParam<'_>> {
594 let Self { path_rows, route, rows, .. } = self;
595 path_rows.iter().filter_map(|PathRow { ident_param_range, row_idx }| {
596 let row = rows.get(usize::from(*row_idx))?;
597 let name = match row.ty {
598 RowTy::Literal => return None,
599 RowTy::Param => {
600 if let [_, name @ .., _] = row.route.as_bytes() {
601 unsafe { str::from_utf8_unchecked(name) }
603 } else {
604 unsafe { unreachable_unchecked() }
606 }
607 }
608 };
609 let value = unsafe {
611 let range = ident_param_range.start.into()..ident_param_range.end.into();
612 str::from_utf8_unchecked(route.get(range)?)
613 };
614 Some(MatcherPathParam::new(name, value))
615 })
616 }
617}
618
619#[derive(Clone, Copy, Debug, PartialEq)]
621pub struct MatcherPathParam<'any> {
622 pub name: &'any str,
624 pub value: &'any str,
626}
627
628impl<'any> MatcherPathParam<'any> {
629 #[inline]
631 pub const fn new(name: &'any str, value: &'any str) -> Self {
632 Self { name, value }
633 }
634}
635
636#[derive(Clone, Copy, Debug, PartialEq)]
637enum CheckSearchRowRslt<'any, T, const MC: usize = DEFAULT_MAX_CHILDREN> {
638 CompleteMatch(&'any T),
639 IncompleteMatch(&'any ArrayVectorU8<Edge, MC>),
640 Mismatch,
641}
642
643#[derive(Clone, Debug, PartialEq)]
644enum CompareRowRslt<const MC: usize = DEFAULT_MAX_CHILDREN> {
645 Finished,
646 RowFull(ArrayVectorU8<Edge, MC>),
647 RowPartial { common_prefix_len: usize, is_single: bool },
648 Unmatched,
649}
650
651#[derive(Clone, Copy, Debug, PartialEq)]
652enum RowTy {
653 Literal,
654 Param,
655}
656
657#[derive(Clone, Copy, Debug, PartialEq)]
659struct Edge {
660 first_byte: Option<u8>,
662 row_target_idx: u8,
663}
664
665impl Edge {
666 #[inline]
667 const fn new(first_byte: Option<u8>, row_target_idx: u8) -> Self {
668 Self { first_byte, row_target_idx }
669 }
670}
671
672#[derive(Clone, Copy, Debug, PartialEq)]
673struct PathRow {
674 ident_param_range: Range<u8>,
675 row_idx: u8,
676}
677
678impl PathRow {
679 #[inline]
680 const fn new(ident_param_range: Range<u8>, row_idx: u8) -> Self {
681 Self { ident_param_range, row_idx }
682 }
683}
684
685#[derive(Clone, Debug, PartialEq)]
692struct Row<T, const MC: usize = DEFAULT_MAX_CHILDREN> {
693 edges: ArrayVectorU8<Edge, MC>,
694 route: ShortBoxStrU8,
695 ty: RowTy,
696 value: Option<T>,
697}
698
699impl<T, const MC: usize> Row<T, MC> {
700 #[inline]
701 const fn new(
702 edges: ArrayVectorU8<Edge, MC>,
703 route: ShortBoxStrU8,
704 ty: RowTy,
705 value: Option<T>,
706 ) -> Self {
707 Self { edges, route, ty, value }
708 }
709}
710
711#[inline]
712fn common_route(
713 common_prefix_len: usize,
714 common_route: &ShortBoxStrU8,
715) -> crate::Result<ShortBoxStrU8> {
716 common_route.get(..common_prefix_len).unwrap_or_default().try_into()
717}
718
719#[inline]
720fn unique_route(
721 common_prefix_len: usize,
722 common_route: &ShortBoxStrU8,
723) -> crate::Result<ShortBoxStrU8> {
724 common_route.get(common_prefix_len..).unwrap_or_default().try_into()
725}