zerotrie/builder/konst/
store.rs

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 ).
4
5//! This module contains internal collections for the const builder.
6
7use super::super::branch_meta::BranchMeta;
8
9/// 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.
15    full_slice: &'a [T],
16    /// The start index of the slice represented by this [`ConstSlice`].
17    start: usize,
18    /// The non-inclusive end index of the slice represented by this [`ConstSlice`].
19    limit: usize,
20}
21
22impl<'a, T> ConstSlice<'a, T> {
23    /// Creates a [`ConstSlice`] representing an entire slice.
24    pub const fn from_slice(other: &'a [T]) -> Self {
25        ConstSlice {
26            full_slice: other,
27            start: 0,
28            limit: other.len(),
29        }
30    }
31
32    /// Creates a [`ConstSlice`] with the given start and limit.
33    pub const fn from_manual_slice(full_slice: &'a [T], start: usize, limit: usize) -> Self {
34        ConstSlice {
35            full_slice,
36            start,
37            limit,
38        }
39    }
40
41    /// Returns the length of the [`ConstSlice`].
42    pub const fn len(&self) -> usize {
43        self.limit - self.start
44    }
45
46    /// Gets the element at `index`, panicking if not present.
47    pub const fn get_or_panic(&self, index: usize) -> &T {
48        #[allow(clippy::indexing_slicing)] // documented
49        &self.full_slice[index + self.start]
50    }
51
52    /// Gets the first element or `None` if empty.
53    #[cfg(test)]
54    pub const fn first(&self) -> Option<&T> {
55        if self.len() == 0 {
56            None
57        } else {
58            // Won't panic: we already handled an empty slice
59            Some(self.get_or_panic(0))
60        }
61    }
62
63    /// Gets the last element or `None` if empty.
64    pub const fn last(&self) -> Option<&T> {
65        if self.len() == 0 {
66            None
67        } else {
68            // Won't panic: we already handled an empty slice
69            Some(self.get_or_panic(self.len() - 1))
70        }
71    }
72
73    /// Gets a subslice of this slice.
74    #[cfg(test)]
75    pub const fn get_subslice_or_panic(
76        &self,
77        new_start: usize,
78        new_limit: usize,
79    ) -> ConstSlice<'a, T> {
80        assert!(new_start <= new_limit);
81        assert!(new_limit <= self.len());
82        ConstSlice {
83            full_slice: self.full_slice,
84            start: self.start + new_start,
85            limit: self.start + new_limit,
86        }
87    }
88
89    /// Non-const function that returns this [`ConstSlice`] as a regular slice.
90    #[cfg(any(test, feature = "alloc"))]
91    #[allow(clippy::indexing_slicing)] // indices in range by struct invariant
92    pub fn as_slice(&self) -> &'a [T] {
93        &self.full_slice[self.start..self.limit]
94    }
95}
96
97impl<'a, T> From<&'a [T]> for ConstSlice<'a, T> {
98    fn from(other: &'a [T]) -> Self {
99        Self::from_slice(other)
100    }
101}
102
103/// A const-friendly mutable data structure backed by an array.
104#[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)]
105pub(crate) struct ConstArrayBuilder<const N: usize, T> {
106    full_array: [T; N],
107    start: usize,
108    limit: usize,
109}
110
111impl<const N: usize, T: Default> Default for ConstArrayBuilder<N, T> {
112    fn default() -> Self {
113        Self::new_empty([(); N].map(|_| Default::default()), 0)
114    }
115}
116
117impl<const N: usize, T> ConstArrayBuilder<N, T> {
118    /// Creates a new, empty builder of the given size. `cursor` indicates where in the
119    /// array new elements will be inserted first. Since we use a lot of prepend operations,
120    /// it is common to set `cursor` to `N`.
121    pub const fn new_empty(full_array: [T; N], cursor: usize) -> Self {
122        if !(cursor <= N) {
    ::core::panicking::panic("assertion failed: cursor <= N")
};assert!(cursor <= N);
123        Self {
124            full_array,
125            start: cursor,
126            limit: cursor,
127        }
128    }
129
130    /// Creates a new builder with some initial content in `[start, limit)`.
131    pub const fn from_manual_slice(full_array: [T; N], start: usize, limit: usize) -> Self {
132        if !(start <= limit) {
    ::core::panicking::panic("assertion failed: start <= limit")
};assert!(start <= limit);
133        if !(limit <= N) { ::core::panicking::panic("assertion failed: limit <= N") };assert!(limit <= N);
134        Self {
135            full_array,
136            start,
137            limit,
138        }
139    }
140
141    /// Returns the number of initialized elements in the builder.
142    pub const fn len(&self) -> usize {
143        self.limit - self.start
144    }
145
146    /// Whether there are no initialized elements in the builder.
147    #[allow(dead_code)]
148    pub const fn is_empty(&self) -> bool {
149        self.len() == 0
150    }
151
152    /// Returns the initialized elements as a [`ConstSlice`].
153    pub const fn as_const_slice(&self) -> ConstSlice<'_, T> {
154        ConstSlice::from_manual_slice(&self.full_array, self.start, self.limit)
155    }
156
157    /// Non-const function that returns a slice of the initialized elements.
158    #[cfg(any(test, feature = "alloc"))]
159    pub fn as_slice(&self) -> &[T] {
160        &self.full_array[self.start..self.limit]
161    }
162}
163
164// Certain functions that involve dropping `T` require that it be `Copy`
165impl<const N: usize, T: Copy> ConstArrayBuilder<N, T> {
166    /// Takes a fully initialized builder as an array. Panics if the builder is not
167    /// fully initialized.
168    pub const fn const_build_or_panic(self) -> [T; N] {
169        if self.start != 0 || self.limit != N {
170            let actual_len = self.limit - self.start;
171            const PREFIX: &[u8; 31] = b"Buffer too large. Size needed: ";
172            let len_bytes: [u8; PREFIX.len() + crate::helpers::MAX_USIZE_LEN_AS_DIGITS] =
173                crate::helpers::const_fmt_int(*PREFIX, actual_len);
174            let Ok(len_str) = core::str::from_utf8(&len_bytes) else {
175                ::core::panicking::panic("internal error: entered unreachable code")unreachable!()
176            };
177            { ::core::panicking::panic_display(&len_str); };panic!("{}", len_str);
178        }
179        self.full_array
180    }
181
182    /// Prepends an element to the front of the builder, panicking if there is no room.
183    #[allow(clippy::indexing_slicing)] // documented
184    pub const fn const_push_front_or_panic(mut self, value: T) -> Self {
185        if self.start == 0 {
186            { ::core::panicking::panic_fmt(format_args!("Buffer too small")); };panic!("Buffer too small");
187        }
188        self.start -= 1;
189        self.full_array[self.start] = value;
190        self
191    }
192
193    /// Prepends multiple elements to the front of the builder, panicking if there is no room.
194    #[allow(clippy::indexing_slicing)] // documented
195    pub const fn const_extend_front_or_panic(mut self, other: ConstSlice<T>) -> Self {
196        if self.start < other.len() {
197            { ::core::panicking::panic_fmt(format_args!("Buffer too small")); };panic!("Buffer too small");
198        }
199        self.start -= other.len();
200        let mut i = self.start;
201        {
    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, {
202            self.full_array[i] = *byte;
203            i += 1;
204        });
205        self
206    }
207}
208
209impl<const N: usize> ConstArrayBuilder<N, u8> {
210    /// Specialized function that performs `self[index] |= bits`
211    #[allow(clippy::indexing_slicing)] // documented
212    pub(crate) const fn const_bitor_assign_or_panic(mut self, index: usize, bits: u8) -> Self {
213        self.full_array[self.start + index] |= bits;
214        self
215    }
216}
217
218impl<const N: usize, T: Copy> ConstArrayBuilder<N, T> {
219    /// Swaps the elements at positions `i` and `j`.
220    #[cfg(feature = "alloc")]
221    pub fn swap_or_panic(mut self, i: usize, j: usize) -> Self {
222        self.full_array.swap(self.start + i, self.start + j);
223        self
224    }
225}
226
227/// Evaluates a block over each element of a const slice. Takes three arguments:
228///
229/// 1. Expression that resolves to the [`ConstSlice`].
230/// 2. Token that will be assigned the value of the element.
231/// 3. Block to evaluate for each element.
232macro_rules! const_for_each {
233    ($safe_const_slice:expr, $item:tt, $inner:expr) => {{
234        let mut i = 0;
235        while i < $safe_const_slice.len() {
236            // Won't panic: in-range loop condition
237            let $item = $safe_const_slice.get_or_panic(i);
238            $inner;
239            i += 1;
240        }
241    }};
242}
243
244pub(crate) use const_for_each;
245
246/// A data structure that holds up to K [`BranchMeta`] items.
247///
248/// Note: It should be possible to store the required data in the builder buffer itself,
249/// which would eliminate the need for this helper struct and the limit it imposes.
250pub(crate) struct ConstLengthsStack<const K: usize> {
251    data: [Option<BranchMeta>; K],
252    idx: usize,
253}
254
255impl<const K: usize> core::fmt::Debug for ConstLengthsStack<K> {
256    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
257        self.as_slice().fmt(f)
258    }
259}
260
261impl<const K: usize> ConstLengthsStack<K> {
262    /// Creates a new empty [`ConstLengthsStack`].
263    pub const fn new() -> Self {
264        Self {
265            data: [None; K],
266            idx: 0,
267        }
268    }
269
270    /// Returns whether the stack is empty.
271    pub const fn is_empty(&self) -> bool {
272        self.idx == 0
273    }
274
275    /// Adds a [`BranchMeta`] to the stack, panicking if there is no room.
276    #[must_use]
277    #[allow(clippy::indexing_slicing)] // documented
278    pub const fn push_or_panic(mut self, meta: BranchMeta) -> Self {
279        if self.idx >= K {
280            {
    ::core::panicking::panic_fmt(format_args!("AsciiTrie Builder: Need more stack (max K)"));
};panic!(concat!(
281                "AsciiTrie Builder: Need more stack (max ",
282                stringify!(K),
283                ")"
284            ));
285        }
286        self.data[self.idx] = Some(meta);
287        self.idx += 1;
288        self
289    }
290
291    /// Returns a copy of the [`BranchMeta`] on the top of the stack, panicking if
292    /// the stack is empty.
293    pub const fn peek_or_panic(&self) -> BranchMeta {
294        if self.idx == 0 {
295            {
    ::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");
296        }
297        self.get_or_panic(0)
298    }
299
300    /// Returns a copy of the [`BranchMeta`] at the specified index.
301    #[allow(clippy::indexing_slicing)] // documented
302    const fn get_or_panic(&self, index: usize) -> BranchMeta {
303        if self.idx <= index {
304            {
    ::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");
305        }
306        match self.data[self.idx - index - 1] {
307            Some(x) => x,
308            None => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
309        }
310    }
311
312    /// Removes many [`BranchMeta`]s from the stack, returning them in a [`ConstArrayBuilder`].
313    #[allow(clippy::indexing_slicing)] // documented
314    pub const fn pop_many_or_panic(
315        mut self,
316        len: usize,
317    ) -> (Self, ConstArrayBuilder<256, BranchMeta>) {
318        if true {
    if !(len <= 256) {
        ::core::panicking::panic("assertion failed: len <= 256")
    };
};debug_assert!(len <= 256);
319        let mut result = ConstArrayBuilder::new_empty([BranchMeta::default(); 256], 256);
320        let mut ix = 0;
321        loop {
322            if ix == len {
323                break;
324            }
325            let i = self.idx - ix - 1;
326            result = result.const_push_front_or_panic(match self.data[i] {
327                Some(x) => x,
328                None => {
    ::core::panicking::panic_fmt(format_args!("Not enough items in the ConstLengthsStack"));
}panic!("Not enough items in the ConstLengthsStack"),
329            });
330            ix += 1;
331        }
332        self.idx -= len;
333        (self, result)
334    }
335
336    /// Non-const function that returns the initialized elements as a slice.
337    fn as_slice(&self) -> &[Option<BranchMeta>] {
338        &self.data[0..self.idx]
339    }
340}
341
342impl<const K: usize> ConstArrayBuilder<K, BranchMeta> {
343    /// Converts this builder-array of [`BranchMeta`] to one of the `ascii` fields.
344    pub const fn map_to_ascii_bytes(&self) -> ConstArrayBuilder<K, u8> {
345        let mut result = ConstArrayBuilder::new_empty([0; K], K);
346        let self_as_slice = self.as_const_slice();
347        {
    let mut i = 0;
    while i < self_as_slice.len() {
        let value = self_as_slice.get_or_panic(i);
        { result = result.const_push_front_or_panic(value.ascii); };
        i += 1;
    }
};const_for_each!(self_as_slice, value, {
348            result = result.const_push_front_or_panic(value.ascii);
349        });
350        result
351    }
352}