1// This file is part of ICU4X. For terms of use, please see the file
2// called LICENSE at the top level of the ICU4X source tree
3// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
45//! This module contains internal collections for the const builder.
67use super::super::branch_meta::BranchMeta;
89/// A const-friendly slice type. It is backed by a full slice but is primarily intended
10/// to represent subslices of the full slice. We need this only because we can't take
11/// subslices in const Rust.
12#[derive(#[automatically_derived]
impl<'a, T: ::core::fmt::Debug> ::core::fmt::Debug for ConstSlice<'a, T> {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field3_finish(f, "ConstSlice",
"full_slice", &self.full_slice, "start", &self.start, "limit",
&&self.limit)
}
}Debug, #[automatically_derived]
impl<'a, T: ::core::marker::Copy> ::core::marker::Copy for ConstSlice<'a, T> {
}Copy, #[automatically_derived]
impl<'a, T: ::core::clone::Clone> ::core::clone::Clone for ConstSlice<'a, T> {
#[inline]
fn clone(&self) -> ConstSlice<'a, T> {
ConstSlice {
full_slice: ::core::clone::Clone::clone(&self.full_slice),
start: ::core::clone::Clone::clone(&self.start),
limit: ::core::clone::Clone::clone(&self.limit),
}
}
}Clone)]
13pub(crate) struct ConstSlice<'a, T> {
14/// The full slice.
15full_slice: &'a [T],
16/// The start index of the slice represented by this [`ConstSlice`].
17start: usize,
18/// The non-inclusive end index of the slice represented by this [`ConstSlice`].
19limit: usize,
20}
2122impl<'a, T> ConstSlice<'a, T> {
23/// Creates a [`ConstSlice`] representing an entire slice.
24pub const fn from_slice(other: &'a [T]) -> Self {
25ConstSlice {
26 full_slice: other,
27 start: 0,
28 limit: other.len(),
29 }
30 }
3132/// Creates a [`ConstSlice`] with the given start and limit.
33pub const fn from_manual_slice(full_slice: &'a [T], start: usize, limit: usize) -> Self {
34ConstSlice {
35full_slice,
36start,
37limit,
38 }
39 }
4041/// Returns the length of the [`ConstSlice`].
42pub const fn len(&self) -> usize {
43self.limit - self.start
44 }
4546/// Gets the element at `index`, panicking if not present.
47pub const fn get_or_panic(&self, index: usize) -> &T {
48#[allow(clippy::indexing_slicing)] // documented
49&self.full_slice[index + self.start]
50 }
5152/// Gets the first element or `None` if empty.
53#[cfg(test)]
54pub const fn first(&self) -> Option<&T> {
55if self.len() == 0 {
56None
57} else {
58// Won't panic: we already handled an empty slice
59Some(self.get_or_panic(0))
60 }
61 }
6263/// Gets a subslice of this slice.
64#[cfg(test)]
65pub const fn get_subslice_or_panic(
66&self,
67 new_start: usize,
68 new_limit: usize,
69 ) -> ConstSlice<'a, T> {
70assert!(new_start <= new_limit);
71assert!(new_limit <= self.len());
72 ConstSlice {
73 full_slice: self.full_slice,
74 start: self.start + new_start,
75 limit: self.start + new_limit,
76 }
77 }
7879/// Non-const function that returns this [`ConstSlice`] as a regular slice.
80#[cfg(any(test, feature = "alloc"))]
81 #[allow(clippy::indexing_slicing)] // indices in range by struct invariant
82pub fn as_slice(&self) -> &'a [T] {
83&self.full_slice[self.start..self.limit]
84 }
85}
8687impl<'a, T> From<&'a [T]> for ConstSlice<'a, T> {
88fn from(other: &'a [T]) -> Self {
89Self::from_slice(other)
90 }
91}
9293/// A const-friendly mutable data structure backed by an array.
94#[derive(#[automatically_derived]
impl<const N : usize, T: ::core::fmt::Debug> ::core::fmt::Debug for
ConstArrayBuilder<N, T> {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field3_finish(f,
"ConstArrayBuilder", "full_array", &self.full_array, "start",
&self.start, "limit", &&self.limit)
}
}Debug, #[automatically_derived]
impl<const N : usize, T: ::core::marker::Copy> ::core::marker::Copy for
ConstArrayBuilder<N, T> {
}Copy, #[automatically_derived]
impl<const N : usize, T: ::core::clone::Clone> ::core::clone::Clone for
ConstArrayBuilder<N, T> {
#[inline]
fn clone(&self) -> ConstArrayBuilder<N, T> {
ConstArrayBuilder {
full_array: ::core::clone::Clone::clone(&self.full_array),
start: ::core::clone::Clone::clone(&self.start),
limit: ::core::clone::Clone::clone(&self.limit),
}
}
}Clone)]
95pub(crate) struct ConstArrayBuilder<const N: usize, T> {
96 full_array: [T; N],
97 start: usize,
98 limit: usize,
99}
100101impl<const N: usize, T: Default> Defaultfor ConstArrayBuilder<N, T> {
102fn default() -> Self {
103Self::new_empty([(); N].map(|_| Default::default()), 0)
104 }
105}
106107impl<const N: usize, T> ConstArrayBuilder<N, T> {
108/// Creates a new, empty builder of the given size. `cursor` indicates where in the
109 /// array new elements will be inserted first. Since we use a lot of prepend operations,
110 /// it is common to set `cursor` to `N`.
111pub const fn new_empty(full_array: [T; N], cursor: usize) -> Self {
112if !(cursor <= N) {
::core::panicking::panic("assertion failed: cursor <= N")
};assert!(cursor <= N);
113Self {
114full_array,
115 start: cursor,
116 limit: cursor,
117 }
118 }
119120/// Creates a new builder with some initial content in `[start, limit)`.
121pub const fn from_manual_slice(full_array: [T; N], start: usize, limit: usize) -> Self {
122if !(start <= limit) {
::core::panicking::panic("assertion failed: start <= limit")
};assert!(start <= limit);
123if !(limit <= N) { ::core::panicking::panic("assertion failed: limit <= N") };assert!(limit <= N);
124Self {
125full_array,
126start,
127limit,
128 }
129 }
130131/// Returns the number of initialized elements in the builder.
132pub const fn len(&self) -> usize {
133self.limit - self.start
134 }
135136/// Whether there are no initialized elements in the builder.
137#[allow(dead_code)]
138pub const fn is_empty(&self) -> bool {
139self.len() == 0
140}
141142/// Returns the initialized elements as a [`ConstSlice`].
143pub const fn as_const_slice(&self) -> ConstSlice<'_, T> {
144ConstSlice::from_manual_slice(&self.full_array, self.start, self.limit)
145 }
146147/// Non-const function that returns a slice of the initialized elements.
148#[cfg(any(test, feature = "alloc"))]
149pub fn as_slice(&self) -> &[T] {
150&self.full_array[self.start..self.limit]
151 }
152}
153154// Certain functions that involve dropping `T` require that it be `Copy`
155impl<const N: usize, T: Copy> ConstArrayBuilder<N, T> {
156/// Takes a fully initialized builder as an array. Panics if the builder is not
157 /// fully initialized.
158pub const fn const_build_or_panic(self) -> [T; N] {
159if self.start != 0 || self.limit != N {
160let actual_len = self.limit - self.start;
161const PREFIX: &[u8; 31] = b"Buffer too large. Size needed: ";
162let len_bytes: [u8; PREFIX.len() + crate::helpers::MAX_USIZE_LEN_AS_DIGITS] =
163crate::helpers::const_fmt_int(*PREFIX, actual_len);
164let Ok(len_str) = core::str::from_utf8(&len_bytes) else {
165::core::panicking::panic("internal error: entered unreachable code")unreachable!()166 };
167{ ::core::panicking::panic_display(&len_str); };panic!("{}", len_str);
168 }
169self.full_array
170 }
171172/// Prepends an element to the front of the builder, panicking if there is no room.
173#[allow(clippy::indexing_slicing)] // documented
174pub const fn const_push_front_or_panic(&mut self, value: T) {
175if self.start == 0 {
176{ ::core::panicking::panic_fmt(format_args!("Buffer too small")); };panic!("Buffer too small");
177 }
178self.start -= 1;
179self.full_array[self.start] = value;
180 }
181182/// Prepends multiple elements to the front of the builder, panicking if there is no room.
183#[allow(clippy::indexing_slicing)] // documented
184pub const fn const_extend_front_or_panic(&mut self, other: ConstSlice<T>) {
185if self.start < other.len() {
186{ ::core::panicking::panic_fmt(format_args!("Buffer too small")); };panic!("Buffer too small");
187 }
188self.start -= other.len();
189let mut i = self.start;
190{
let mut i = 0;
while i < other.len() {
let byte = other.get_or_panic(i);
{ self.full_array[i] = *byte; i += 1; };
i += 1;
}
};const_for_each!(other, byte, {
191self.full_array[i] = *byte;
192 i += 1;
193 });
194 }
195}
196197impl<const N: usize> ConstArrayBuilder<N, u8> {
198/// Specialized function that performs `self[index] |= bits`
199#[allow(clippy::indexing_slicing)] // documented
200pub(crate) const fn const_bitor_assign_or_panic(&mut self, index: usize, bits: u8) {
201self.full_array[self.start + index] |= bits;
202 }
203}
204205impl<const N: usize, T: Copy> ConstArrayBuilder<N, T> {
206/// Swaps the elements at positions `i` and `j`.
207#[cfg(feature = "alloc")]
208pub fn swap_or_panic(&mut self, i: usize, j: usize) {
209self.full_array.swap(self.start + i, self.start + j);
210 }
211}
212213/// Evaluates a block over each element of a const slice. Takes three arguments:
214///
215/// 1. Expression that resolves to the [`ConstSlice`].
216/// 2. Token that will be assigned the value of the element.
217/// 3. Block to evaluate for each element.
218macro_rules! const_for_each {
219 ($safe_const_slice:expr, $item:tt, $inner:expr) => {{
220let mut i = 0;
221while i < $safe_const_slice.len() {
222// Won't panic: in-range loop condition
223let $item = $safe_const_slice.get_or_panic(i);
224$inner;
225 i += 1;
226 }
227 }};
228}
229230pub(crate) use const_for_each;
231232/// A data structure that holds up to K [`BranchMeta`] items.
233///
234/// Note: It should be possible to store the required data in the builder buffer itself,
235/// which would eliminate the need for this helper struct and the limit it imposes.
236pub(crate) struct ConstLengthsStack<const K: usize> {
237 data: [Option<BranchMeta>; K],
238 idx: usize,
239}
240241impl<const K: usize> core::fmt::Debugfor ConstLengthsStack<K> {
242fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
243self.as_slice().fmt(f)
244 }
245}
246247impl<const K: usize> ConstLengthsStack<K> {
248/// Creates a new empty [`ConstLengthsStack`].
249pub const fn new() -> Self {
250Self {
251 data: [None; K],
252 idx: 0,
253 }
254 }
255256/// Returns whether the stack is empty.
257pub const fn is_empty(&self) -> bool {
258self.idx == 0
259}
260261/// Adds a [`BranchMeta`] to the stack, panicking if there is no room.
262#[allow(clippy::indexing_slicing)] // documented
263pub const fn push_or_panic(&mut self, meta: BranchMeta) {
264if self.idx >= K {
265{
::core::panicking::panic_fmt(format_args!("AsciiTrie Builder: Need more stack (max K)"));
};panic!(concat!(
266"AsciiTrie Builder: Need more stack (max ",
267stringify!(K),
268")"
269));
270 }
271self.data[self.idx] = Some(meta);
272self.idx += 1;
273 }
274275/// Returns a copy of the [`BranchMeta`] on the top of the stack, panicking if
276 /// the stack is empty.
277pub const fn peek_or_panic(&self) -> BranchMeta {
278if self.idx == 0 {
279{
::core::panicking::panic_fmt(format_args!("AsciiTrie Builder: Attempted to peek from an empty stack"));
};panic!("AsciiTrie Builder: Attempted to peek from an empty stack");
280 }
281self.get_or_panic(0)
282 }
283284/// Returns a copy of the [`BranchMeta`] at the specified index.
285#[allow(clippy::indexing_slicing)] // documented
286const fn get_or_panic(&self, index: usize) -> BranchMeta {
287if self.idx <= index {
288{
::core::panicking::panic_fmt(format_args!("AsciiTrie Builder: Attempted to get too deep in a stack"));
};panic!("AsciiTrie Builder: Attempted to get too deep in a stack");
289 }
290match self.data[self.idx - index - 1] {
291Some(x) => x,
292None => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
293 }
294 }
295296/// Removes many [`BranchMeta`]s from the stack, returning them in a [`ConstArrayBuilder`].
297#[allow(clippy::indexing_slicing)] // documented
298pub const fn pop_many_or_panic(&mut self, len: usize) -> ConstArrayBuilder<256, BranchMeta> {
299if true {
if !(len <= 256) {
::core::panicking::panic("assertion failed: len <= 256")
};
};debug_assert!(len <= 256);
300let mut result = ConstArrayBuilder::new_empty([BranchMeta::default(); 256], 256);
301let mut ix = 0;
302loop {
303if ix == len {
304break;
305 }
306let i = self.idx - ix - 1;
307result.const_push_front_or_panic(match self.data[i] {
308Some(x) => x,
309None => {
::core::panicking::panic_fmt(format_args!("Not enough items in the ConstLengthsStack"));
}panic!("Not enough items in the ConstLengthsStack"),
310 });
311ix += 1;
312 }
313self.idx -= len;
314result315 }
316317/// Non-const function that returns the initialized elements as a slice.
318fn as_slice(&self) -> &[Option<BranchMeta>] {
319&self.data[0..self.idx]
320 }
321}
322323impl<const K: usize> ConstArrayBuilder<K, BranchMeta> {
324/// Converts this builder-array of [`BranchMeta`] to one of the `ascii` fields.
325pub const fn map_to_ascii_bytes(&self) -> ConstArrayBuilder<K, u8> {
326let mut result = ConstArrayBuilder::new_empty([0; K], K);
327let self_as_slice = self.as_const_slice();
328{
let mut i = 0;
while i < self_as_slice.len() {
let value = self_as_slice.get_or_panic(i);
{ result.const_push_front_or_panic(value.ascii); };
i += 1;
}
};const_for_each!(self_as_slice, value, {
329 result.const_push_front_or_panic(value.ascii);
330 });
331result332 }
333}