StratoSDK is a framework with a declarative approach similar to Flutter/React, written and designed entirely for Rust.
| 1 | //! Module containing the definition of [`StrIndexMap`], allowing for repeated, efficient conversion |
| 2 | //! between a byte and/or char index from a backing `str`. |
| 3 | |
| 4 | use std::collections::HashMap; |
| 5 | |
| 6 | /// Map that provides efficient conversion from byte <-> char index from a backing `str`. |
| 7 | /// See [`StrIndexMap::byte_index`] and [`StrIndexMap::char_index`] for conversion functions to |
| 8 | /// convert from/to a byte index to a char index. |
| 9 | pub(super) struct StrIndexMap { |
| 10 | byte_to_char_index: HashMap<usize, usize>, |
| 11 | char_to_byte_index: Vec<usize>, |
| 12 | } |
| 13 | |
| 14 | impl StrIndexMap { |
| 15 | /// Constructs a new [`StrIndexMap`] with byte <-> char indices based on the input `str`. |
| 16 | /// NOTE this runs in O(n) time as it requires walking through each char index in the `str`. |
| 17 | pub(super) fn new(str: impl AsRef<str>) -> Self { |
| 18 | let char_indices = str.as_ref().char_indices(); |
| 19 | let (_, upper_bound) = char_indices.size_hint(); |
| 20 | |
| 21 | let (mut byte_to_char_index, mut char_to_byte_index) = match upper_bound { |
| 22 | None => (HashMap::new(), Vec::new()), |
| 23 | Some(size) => (HashMap::with_capacity(size), Vec::with_capacity(size)), |
| 24 | }; |
| 25 | |
| 26 | for (char_index, (byte_index, _)) in char_indices.enumerate() { |
| 27 | byte_to_char_index.insert(byte_index, char_index); |
| 28 | char_to_byte_index.push(byte_index); |
| 29 | } |
| 30 | |
| 31 | Self { |
| 32 | byte_to_char_index, |
| 33 | char_to_byte_index, |
| 34 | } |
| 35 | } |
| 36 | |
| 37 | /// Returns the _byte_ index of the string at the given `char_index`. If the `char_index` does |
| 38 | /// not exist in the string, `None` is returned. |
| 39 | pub(super) fn byte_index(&self, char_index: usize) -> Option<usize> { |
| 40 | self.char_to_byte_index.get(char_index).copied() |
| 41 | } |
| 42 | |
| 43 | /// Returns the _char_ index of the string at the given byte index. If the `byte_index` does not |
| 44 | /// exist in the string or if it does not lie at a char boundary, `None` is returned. |
| 45 | pub(super) fn char_index(&self, byte_index: usize) -> Option<usize> { |
| 46 | self.byte_to_char_index.get(&byte_index).copied() |
| 47 | } |
| 48 | |
| 49 | /// Returns the total number of characters in the string. |
| 50 | pub(super) fn num_chars(&self) -> usize { |
| 51 | self.char_to_byte_index.len() |
| 52 | } |
| 53 | } |
| 54 | |
| 55 | #[cfg(test)] |
| 56 | #[path = "str_index_map_tests.rs"] |
| 57 | mod tests; |
| 58 |