utf8_rune/
heuristic.rs

1//! heuristic functions to find UTF-8 "[runes](crate::Rune)" within raw [u8] pointers
2use crate::pointer::{
3    self, get_byte_at_index, get_byte_slice_of, is_valid_utf8_str_of,
4};
5use crate::{ByteType, Error, Result};
6
7/// heuristic function that determines the cutoff index at which a
8/// "[rune](crate::Rune)" ends after the given index.
9///
10/// # Example
11///
12/// > Note: `utf8_rune::mem` requires the `mem` feature.
13///
14/// ```
15/// use utf8_rune::pointer::{self, get_byte_slice_of};
16/// use utf8_rune::{get_rune_cutoff_at_index, Result};
17///
18/// let bytes = "πŸ‘©πŸ»β€πŸš’πŸ‘ŒπŸΏπŸ§‘πŸ½β€πŸš’πŸ‘¨β€πŸš’πŸŒΆοΈπŸŽΉπŸ’”πŸ”₯❀️‍πŸ”₯β€οΈβ€πŸ©Ή".as_bytes();
19/// let length = bytes.len();
20/// let ptr = bytes.as_ptr();
21///
22/// let index = 56;
23/// let cutoff = get_rune_cutoff_at_index(ptr, length, index).unwrap();
24/// let count = cutoff - index;
25/// let slice = get_byte_slice_of(ptr, index, count);
26/// assert_eq!(slice, "🎹".as_bytes());
27/// ```
28#[inline]
29pub fn get_rune_cutoff_at_index<'g>(
30    ptr: *const u8,
31    length: usize,
32    index: usize,
33) -> Result<usize> {
34    let ptr = pointer::copy(ptr, length)?;
35
36    if index > length {
37        return Err(Error::InvalidIndex(index, get_byte_slice_of(ptr, 0, length)));
38    }
39    if length == 0 {
40        return Ok(index + length);
41    }
42    let index = index;
43    let cutoff = index + 1;
44    let byte = get_byte_at_index(ptr, index);
45    let ty = ByteType::from(byte);
46    let max = if ty.is_ascii() {
47        return Ok(cutoff);
48    } else if ty.is_continuation() {
49        return Err(unexpected_continuation_byte_at_index_error(ptr, length, index));
50    } else {
51        ty.len()
52    };
53    let mut cutoff = index + max;
54    let mut max = index + max;
55    let charmax = index + 6; // +6 octets
56    if cutoff <= max && max < length {
57        while max < charmax && cutoff <= max && max < length {
58            let next_byte = get_byte_at_index(ptr, max);
59            let next_ty = ByteType::from(next_byte);
60            let tcutoff = cutoff + next_ty.len();
61            if let Some((count, cty)) = continuation_bytes_location(ptr, length, cutoff)
62            {
63                let tcutoff = cutoff + count;
64                if is_valid_utf8_str_of(ptr, index, tcutoff - index) {
65                    cutoff = tcutoff;
66                    break;
67                } else {
68                    max += cty.len();
69                }
70            } else if next_ty.has_rune_delta() {
71                if next_ty.len() < 4 {
72                    if let Some((_count_, _cty_)) =
73                        continuation_bytes_location(ptr, length, cutoff - index)
74                    {
75                        cutoff += next_ty.len();
76                    }
77                    break;
78                } else if let Some((count, cty)) =
79                    continuation_bytes_location(ptr, length, tcutoff)
80                {
81                    let tcutoff = cutoff + count + cty.len();
82                    cutoff = tcutoff;
83                    break;
84                } else {
85                    let delta = tcutoff - cutoff;
86                    match delta {
87                        4 => {
88                            let mut next_chunk = [0u8; 4];
89                            let next_bytes = get_byte_slice_of(
90                                ptr,
91                                index + delta,
92                                cutoff - index + delta,
93                            );
94                            next_chunk.copy_from_slice(&next_bytes[0..4]);
95                            match next_chunk {
96                                [0xF0, 0x9F, 0x8F, 0xBB]
97                                | [0xF0, 0x9F, 0x8F, 0xBC]
98                                | [0xF0, 0x9F, 0x8F, 0xBD]
99                                | [0xF0, 0x9F, 0x8F, 0xBE]
100                                | [0xF0, 0x9F, 0x8F, 0xBF] => {
101                                    cutoff += delta;
102                                    break;
103                                },
104                                _ => {},
105                            }
106                        },
107                        _ => {},
108                    }
109
110                    break;
111                }
112            } else if next_ty.is_ascii() {
113                break;
114            } else {
115            }
116            cutoff += 1;
117        }
118    }
119    return Ok(cutoff);
120}
121/// equivalent to calling [`get_rune_cutoff_at_index`] with index 0
122///
123/// # Example
124///
125/// ```
126/// use utf8_rune::split_at_first_rune;
127/// let bytes = "☠️skull".as_bytes();
128/// let length = bytes.len();
129/// let ptr = bytes.as_ptr();
130/// assert_eq!(split_at_first_rune(ptr, length), 6);
131/// assert_eq!(std::str::from_utf8(&bytes[0..6]), Ok("☠️"));
132/// ```
133#[inline]
134pub fn split_at_first_rune<'g>(ptr: *const u8, length: usize) -> usize {
135    get_rune_cutoff_at_index(ptr, length, 0).expect("should not fail at index 0")
136}
137
138/// returns the byte count until the end of that sequence the
139/// [ByteType](crate::ByteType) corresponding to the first byte
140/// pointed at by index when called with index that points to a
141/// sequence of bytes equivalent to either `U+200D` or `U+FE0F`.
142///
143/// # Example
144///
145/// ```
146/// use utf8_rune::continuation_bytes_location;
147///
148/// let bytes = "πŸ‘©πŸ»β€πŸš’".as_bytes();
149/// let index = 8;
150/// let length = bytes.len();
151/// let ptr = bytes.as_ptr();
152/// let (count, ty) = continuation_bytes_location(ptr, length, index).unwrap();
153/// assert_eq!(count, 7);
154/// assert_eq!(&bytes[index..(index+count)], &[0xE2, 0x80, 0x8D, 0xF0, 0x9F, 0x9A, 0x92]);
155/// assert_eq!(std::str::from_utf8(&bytes[index..(index+count)]), Ok("\u{200d}πŸš’"));
156/// ```
157///
158/// ```
159/// use utf8_rune::continuation_bytes_location;
160///
161/// let bytes = "❀️‍πŸ”₯".as_bytes();
162/// let index = 3;
163/// let length = bytes.len();
164/// let ptr = bytes.as_ptr();
165/// let (count, ty) = continuation_bytes_location(ptr, length, index).unwrap();
166/// assert_eq!(count, 10);
167/// assert_eq!(&bytes[index..(index+count)], &[0xEF, 0xB8, 0x8F, 0xE2, 0x80, 0x8D,
168///     0xF0, 0x9F, 0x94, 0xA5
169/// ]);
170/// assert_eq!(std::str::from_utf8(&bytes[index..(index+count)]), Ok("\u{fe0f}\u{200d}πŸ”₯"));
171/// ```
172///
173#[inline]
174pub fn continuation_bytes_location<'g>(
175    ptr: *const u8,
176    length: usize,
177    index: usize,
178) -> Option<(usize, ByteType)> {
179    let shift: usize = (0xE2u8.leading_ones() & 0xEFu8.leading_ones()) as usize;
180    if index + (shift - 1) < length {
181        let zwj0 = get_byte_at_index(ptr, index);
182        let zwj1 = get_byte_at_index(ptr, index + 1);
183        let zwj2 = get_byte_at_index(ptr, index + 2);
184        let next_rune_byte = get_byte_at_index(ptr, index + shift);
185        let ty = if index + shift < length {
186            ByteType::from(next_rune_byte)
187        } else {
188            ByteType::None
189        };
190        let tuple = (zwj0, zwj1, zwj2);
191        match tuple {
192            (0xE2, 0x80, 0x8D) => {
193                let count = shift + ty.len();
194                Some((count, ty))
195            },
196            (0xEF, 0xB8, 0x8F) => {
197                let mut count = 0xEFu8.leading_ones() as usize;
198                if let Some((next_count, _ty_)) =
199                    continuation_bytes_location(ptr, length, index + shift)
200                {
201                    count += next_count
202                }
203
204                Some((count, ty))
205            },
206            _ => None,
207        }
208    } else {
209        None
210    }
211}
212
213#[inline]
214pub(crate) fn previous_valid_cutoff<'e>(
215    ptr: *const u8,
216    length: usize,
217    index: usize,
218) -> Option<usize> {
219    let ptr = pointer::copy(ptr, length).unwrap();
220    if index == 0 && index == length {
221        return None;
222    }
223    let mut previous_index = index;
224    let mut byte = get_byte_at_index(ptr, previous_index);
225    #[allow(unused_assignments)]
226    let mut ty = ByteType::from(byte);
227
228    while previous_index > 0 {
229        ty = ByteType::from(byte);
230        if !ty.has_rune_delta() {
231            previous_index -= 1;
232            byte = get_byte_at_index(ptr, previous_index);
233        } else {
234            break;
235        }
236    }
237    if previous_index == length {
238        None
239    } else {
240        byte = get_byte_at_index(ptr, previous_index);
241        if let Some((count, cty)) =
242            continuation_bytes_location(ptr, length, previous_index)
243        {
244            if previous_index >= cty.len() {
245                if is_valid_utf8_str_of(ptr, previous_index - cty.len(), count) {
246                    previous_index -= cty.len();
247                    Some(previous_index)
248                } else {
249                    previous_index -= 1;
250                    previous_valid_cutoff(ptr, length, previous_index)
251                }
252            } else {
253                if previous_index > 0 && previous_index <= cty.len() {
254                    previous_index -= 1;
255                    return previous_valid_cutoff(ptr, length, previous_index);
256                }
257
258                Some(previous_index)
259            }
260        } else if ByteType::from(byte).has_rune_delta() {
261            let cty = ByteType::from(byte);
262            if is_valid_utf8_str_of(ptr, previous_index, length - previous_index) {
263                return Some(previous_index);
264            } else if previous_index >= cty.len() {
265                previous_index -= 1;
266                return previous_valid_cutoff(ptr, length, previous_index);
267            } else {
268                Some(previous_index)
269            }
270        } else if previous_index == 0 {
271            None
272        } else {
273            Some(previous_index)
274        }
275    }
276}
277
278#[inline]
279pub(crate) fn next_valid_cutoff<'e>(
280    ptr: *const u8,
281    length: usize,
282    index: usize,
283) -> Option<usize> {
284    if length == 0 {
285        return None;
286    }
287    if index >= length {
288        return None;
289    }
290    let ptr = pointer::copy(ptr, length).unwrap();
291    let mut next_index = index;
292
293    while next_index < length {
294        if let Some((count, _ty_)) =
295            continuation_bytes_location(ptr, length, next_index)
296        {
297            next_index += count;
298            break;
299        } else if let Some((count, _ty_)) =
300            continuation_bytes_location(ptr, length, next_index + 1)
301        {
302            next_index += count + 1;
303            break;
304        } else {
305            let byte = get_byte_at_index(ptr, next_index);
306            let ty = ByteType::from(byte);
307            if ty.has_rune_delta() {
308                break;
309            } else if ty.is_continuation() {
310                next_index += 1;
311            } else {
312                break;
313            }
314        }
315    }
316    if next_index == length {
317        None
318    } else {
319        Some(next_index)
320    }
321}
322
323pub(crate) fn unexpected_continuation_byte_at_index_error<'e>(
324    ptr: *const u8,
325    length: usize,
326    index: usize,
327) -> Error<'e> {
328    let byte = get_byte_at_index(ptr, index);
329    let previous_index = previous_valid_cutoff(ptr, length, index);
330    let next_index = next_valid_cutoff(ptr, length, index);
331    let slice = get_byte_slice_of(ptr, index, length);
332    Error::UnexpectedContinuationByte(byte, index, previous_index, next_index, slice)
333}
334
335#[cfg(test)]
336mod test_split_at_first_rune {
337    use crate::pointer::{self};
338    use crate::{split_at_first_rune, Result};
339
340    #[test]
341    fn test_split_at_first_rune_4_bytes() -> Result<()> {
342        //  "πŸ˜€" => [0xf0, 0x9f, 0x98, 0x80] => [0b11110000, 0b10011111, 0b10011000, 0b10000000]
343        let (ptr, length) = pointer::from_slice("πŸ˜€smiley".as_bytes())?;
344        let cutoff = split_at_first_rune(ptr, length);
345        assert_eq!(cutoff, 4);
346        assert_eq!(length, 10);
347
348        Ok(())
349    }
350
351    #[test]
352    fn test_split_at_first_rune_6_bytes() -> Result<()> {
353        // "☠️" => [0xe2, 0x98, 0xa0, 0xef, 0xb8, 0x8f] => [0b11100010, 0b10011000, 0b10100000, 0b11101111, 0b10111000, 0b10001111]
354        let (ptr, length) = pointer::from_slice("☠️skull".as_bytes())?;
355        let cutoff = split_at_first_rune(ptr, length);
356        assert_eq!(length, 11);
357        assert_eq!(cutoff, 6);
358
359        Ok(())
360    }
361
362    #[test]
363    fn test_split_at_first_ascii() -> Result<()> {
364        let (ptr, length) = pointer::from_slice("abcdefghijklmnopqrstu".as_bytes())?;
365        let cutoff = split_at_first_rune(ptr, length);
366        assert_eq!(cutoff, 1);
367        assert_eq!(length, 21);
368
369        Ok(())
370    }
371
372    #[test]
373    fn test_split_at_first_nonascii_single_byte_character() -> Result<()> {
374        let (ptr, length) = pointer::from_slice("Γ£o".as_bytes())?;
375        let cutoff = split_at_first_rune(ptr, length);
376        assert_eq!(cutoff, 2);
377        assert_eq!(length, 3);
378
379        Ok(())
380    }
381
382    #[test]
383    fn test_split_at_first_rune_single_heart() -> Result<()> {
384        let (ptr, length) = pointer::from_slice("❀️".as_bytes())?;
385        let cutoff = split_at_first_rune(ptr, length);
386        assert_eq!(cutoff, 6);
387        assert_eq!(length, 6);
388        Ok(())
389    }
390
391    #[test]
392    fn test_split_at_first_rune_two_to_vec() -> Result<()> {
393        let (ptr, length) = pointer::from_slice("β€οΈπŸ¦…".as_bytes())?;
394        let cutoff = split_at_first_rune(ptr, length);
395        assert_eq!(cutoff, 6);
396        assert_eq!(length, 10);
397        Ok(())
398    }
399}
400
401#[cfg(test)]
402mod test_get_rune_cutoff_at_index {
403    use crate::pointer::{self};
404    use crate::{assert_get_rune_cutoff_at_index, get_rune_cutoff_at_index, Result};
405    #[test]
406    fn test_get_rune_cutoff_at_first_index_single_rune() -> Result<()> {
407        let (ptr, length) = pointer::from_slice("❀️".as_bytes())?;
408        assert_get_rune_cutoff_at_index!(ptr, length, 6, 0, 6, "❀️");
409        Ok(())
410    }
411
412    #[test]
413    fn test_get_rune_cutoff_empty() -> Result<()> {
414        let (ptr, length) = pointer::from_slice("".as_bytes())?;
415        assert_get_rune_cutoff_at_index!(ptr, length, 0, 0, 0, "");
416        Ok(())
417    }
418
419    #[test]
420    fn test_get_rune_cutoff_at_various_indexes_4_bytes() -> Result<()> {
421        //  "πŸ˜€" => [0xf0, 0x9f, 0x98, 0x80] => [0b11110000, 0b10011111, 0b10011000, 0b10000000]
422        let (ptr, length) = pointer::from_slice("smileyπŸ˜€smiley".as_bytes())?;
423        assert_get_rune_cutoff_at_index!(ptr, length, 16, 6, 10, "πŸ˜€");
424
425        Ok(())
426    }
427
428    #[test]
429    fn test_get_rune_cutoff_at_various_indexes_6_bytes() -> Result<()> {
430        // "☠️" => [0xe2, 0x98, 0xa0, 0xef, 0xb8, 0x8f] => [0b11100010, 0b10011000, 0b10100000, 0b11101111, 0b10111000, 0b10001111]
431        let (ptr, length) = pointer::from_slice("skull☠️skull".as_bytes())?;
432        assert_get_rune_cutoff_at_index!(ptr, length, 16, 5, 11, "☠️");
433
434        Ok(())
435    }
436
437    #[test]
438    fn test_get_rune_cutoff_at_various_indexes_ascii() -> Result<()> {
439        let (ptr, length) = pointer::from_slice("abcdefghijklmnopqrstu".as_bytes())?;
440        assert_get_rune_cutoff_at_index!(ptr, length, 21, 7, 8, "h");
441
442        Ok(())
443    }
444
445    #[test]
446    fn test_get_rune_cutoff_at_various_indexes_non_ascii() -> Result<()> {
447        // "πŸ¦…" => length=4 => [0xf0, 0x9f, 0xa6, 0x85] => [0b11110000, 0b10011111, 0b10100110, 0b10000101] => [240, 159, 166, 133]
448        // "Γ£" => length=2 => [0xc3, 0xa3] => [0b11000011, 0b10100011] => [195, 163]
449
450        let (ptr, length) = pointer::from_slice("falcΓ£oπŸ¦…".as_bytes())?;
451        assert_get_rune_cutoff_at_index!(ptr, length, 11, 4, 6, "Γ£");
452        assert_get_rune_cutoff_at_index!(ptr, length, 11, 6, 7, "o");
453        assert_get_rune_cutoff_at_index!(ptr, length, 11, 7, 11, "πŸ¦…");
454        Ok(())
455    }
456
457    #[test]
458    fn test_get_rune_cutoff_at_first_index() -> Result<()> {
459        let (ptr, length) = pointer::from_slice("β€οΈπŸ¦…".as_bytes())?;
460        assert_get_rune_cutoff_at_index!(ptr, length, 10, 0, 6, "❀️");
461        assert_get_rune_cutoff_at_index!(ptr, length, 10, 6, 10, "πŸ¦…");
462        Ok(())
463    }
464
465    #[test]
466    fn test_get_rune_cutoff_unexpected_continuation_byte() -> Result<()> {
467        let (ptr, _) = pointer::from_slice("β€οΈπŸ¦…".as_bytes())?;
468        let cutoff = get_rune_cutoff_at_index(ptr, 10, 4);
469
470        assert!(cutoff.is_err());
471        let err = cutoff.err().unwrap();
472        assert_eq!(err.previous_valid_cutoff(), Some(0));
473        assert_eq!(err.next_valid_cutoff(), Some(6));
474        Ok(())
475    }
476
477    #[test]
478    fn test_get_rune_cutoff_at_various_indexes_94_bytes() -> Result<()> {
479        // "πŸ‘©πŸ»β€πŸš’" => length=15 => [0xf0, 0x9f, 0x91, 0xa9, 0xf0, 0x9f, 0x8f, 0xbb, 0xe2, 0x80, 0x8d, 0xf0, 0x9f, 0x9a, 0x92] =>
480        // [0b11110000, 0b10011111, 0b10010001, 0b10101001, 0b11110000, 0b10011111, 0b10001111, 0b10111011,
481        //  0b11100010, 0b10000000, 0b10001101, 0b11110000, 0b10011111, 0b10011010, 0b10010010] => [240, 159, 145, 169, 240, 159, 143, 187, 226, 128, 141, 240, 159, 154, 146]
482        // "πŸ‘ŒπŸΏ" => length=8 => [0xf0, 0x9f, 0x91, 0x8c, 0xf0, 0x9f, 0x8f, 0xbf] =>
483        // [0b11110000, 0b10011111, 0b10010001, 0b10001100, 0b11110000, 0b10011111, 0b10001111, 0b10111111] => [240, 159, 145, 140, 240, 159, 143, 191]
484        // "πŸ§‘πŸ½β€πŸš’" => length=15 => [0xf0, 0x9f, 0xa7, 0x91, 0xf0, 0x9f, 0x8f, 0xbd, 0xe2, 0x80, 0x8d, 0xf0, 0x9f, 0x9a, 0x92] => [0b11110000, 0b10011111, 0b10100111, 0b10010001, 0b11110000, 0b10011111, 0b10001111, 0b10111101, 0b11100010, 0b10000000, 0b10001101, 0b11110000, 0b10011111, 0b10011010, 0b10010010] => [240, 159, 167, 145, 240, 159, 143, 189, 226, 128, 141, 240, 159, 154, 146]
485        // "πŸ‘¨β€πŸš’" => length=11 => [0xf0, 0x9f, 0x91, 0xa8, 0xe2, 0x80, 0x8d, 0xf0, 0x9f, 0x9a, 0x92] => [0b11110000, 0b10011111, 0b10010001, 0b10101000, 0b11100010, 0b10000000, 0b10001101, 0b11110000, 0b10011111, 0b10011010, 0b10010010] => [240, 159, 145, 168, 226, 128, 141, 240, 159, 154, 146]
486        // "🌢️" => length=7 => [0xf0, 0x9f, 0x8c, 0xb6, 0xef, 0xb8, 0x8f] =>
487        // [0b11110000, 0b10011111, 0b10001100, 0b10110110, 0b11101111, 0b10111000, 0b10001111] => [240, 159, 140, 182, 239, 184, 143]
488        // "🎹" => length=4 => [0xf0, 0x9f, 0x8e, 0xb9] => [0b11110000, 0b10011111, 0b10001110, 0b10111001] => [240, 159, 142, 185]
489        // "πŸ’”" => length=4 => [0xf0, 0x9f, 0x92, 0x94] => [0b11110000, 0b10011111, 0b10010010, 0b10010100] => [240, 159, 146, 148]
490        // "πŸ”₯" => length=4 => [0xf0, 0x9f, 0x94, 0xa5] => [0b11110000, 0b10011111, 0b10010100, 0b10100101] => [240, 159, 148, 165]
491        // "❀️‍πŸ”₯" => length=13 => [0xe2, 0x9d, 0xa4, 0xef, 0xb8, 0x8f, 0xe2, 0x80, 0x8d, 0xf0, 0x9f, 0x94, 0xa5] => [0b11100010, 0b10011101, 0b10100100, 0b11101111, 0b10111000, 0b10001111, 0b11100010, 0b10000000, 0b10001101, 0b11110000, 0b10011111, 0b10010100, 0b10100101] => [226, 157, 164, 239, 184, 143, 226, 128, 141, 240, 159, 148, 165]
492        // "β€οΈβ€πŸ©Ή" => length=13 => [0xe2, 0x9d, 0xa4, 0xef, 0xb8, 0x8f, 0xe2, 0x80, 0x8d, 0xf0, 0x9f, 0xa9, 0xb9] => [0b11100010, 0b10011101, 0b10100100, 0b11101111, 0b10111000, 0b10001111, 0b11100010, 0b10000000, 0b10001101, 0b11110000, 0b10011111, 0b10101001, 0b10111001] => [226, 157, 164, 239, 184, 143, 226, 128, 141, 240, 159, 169, 185]
493        let (ptr, length) = pointer::from_slice("πŸ‘©πŸ»β€πŸš’πŸ‘ŒπŸΏπŸ§‘πŸ½β€πŸš’πŸ‘¨β€πŸš’πŸŒΆοΈπŸŽΉπŸ’”πŸ”₯❀️‍πŸ”₯β€οΈβ€πŸ©Ή".as_bytes())?;
494        assert_get_rune_cutoff_at_index!(ptr, length, 94, 0, 15, "πŸ‘©πŸ»β€πŸš’");
495        assert_get_rune_cutoff_at_index!(ptr, length, 94, 15, 23, "πŸ‘ŒπŸΏ");
496        assert_get_rune_cutoff_at_index!(ptr, length, 94, 23, 38, "πŸ§‘πŸ½β€πŸš’");
497        assert_get_rune_cutoff_at_index!(ptr, length, 94, 38, 49, "πŸ‘¨β€πŸš’");
498        assert_get_rune_cutoff_at_index!(ptr, length, 94, 49, 56, "🌢️");
499        assert_get_rune_cutoff_at_index!(ptr, length, 94, 56, 60, "🎹");
500        assert_get_rune_cutoff_at_index!(ptr, length, 94, 60, 64, "πŸ’”");
501        assert_get_rune_cutoff_at_index!(ptr, length, 94, 64, 68, "πŸ”₯");
502        assert_get_rune_cutoff_at_index!(ptr, length, 94, 68, 81, "❀️‍πŸ”₯");
503        assert_get_rune_cutoff_at_index!(ptr, length, 94, 81, 94, "β€οΈβ€πŸ©Ή");
504
505        Ok(())
506    }
507    #[macro_export]
508    macro_rules! assert_get_rune_cutoff_at_index {
509    (
510        $ptr:expr,
511        $length:expr,
512        $expected_length:literal,
513        $index:literal,
514        $cutoff:literal,
515        $expected:literal
516        $(,)?
517    ) => {{
518        use debug_et_diagnostics::{ansi, fore, from_bytes, indent};
519        use crate::{get_byte_slice_of, format_bytes, RuneParts};
520
521        // debug_et_diagnostics::step!(fg=line, format!("expecting {} from index..cutoff {}..{}", $expected, $index, $cutoff));
522
523        let slice = get_byte_slice_of($ptr, 0, $length)
524            .iter()
525            .map(Clone::clone)
526            .map(|c| fore(c.to_string(), from_bytes(&[c]).into()))
527            .collect::<Vec<String>>()
528            .join(", ");
529        let cutoff = get_rune_cutoff_at_index($ptr, $length, $index)?;
530        let count = $cutoff - $index;
531        assert_eq!(
532            $length,
533            $expected_length,
534            "{}",
535            [
536                fore("expected length to be", 231),
537                fore(format!("{}", $expected_length), 196),
538            ]
539            .join(" ")
540        );
541        assert_eq!(
542            cutoff,
543            $cutoff,
544            "{}",
545            [
546                fore("expected cutoff", 231),
547                fore(format!("{}", cutoff), 220),
548                fore("to be", 231),
549                fore(format!("{}", $cutoff), 196),
550                fore("so as to match rune:", 231),
551                ansi(format!("{}", $expected), 16, 231),
552                format_expected_rune($expected),
553                fore("instead of:", 231),
554                ansi(format!("{}", {
555                    let slice = get_byte_slice_of($ptr, $index, cutoff);
556                    let string = std::str::from_utf8(slice)
557                        .map(|c| ansi(format!("{c}"), 16, 231))
558                        .unwrap_or_else(|e| {
559                            let slice = get_byte_slice_of($ptr, $index, e.valid_up_to());
560                            std::str::from_utf8(slice).map(String::from).unwrap_or_default()
561                        });
562                    string
563                }), 16, 231),
564                {
565                    format!(
566                        "\n{}\n",
567                        [
568                            String::new(),
569                            fore(".i.e.:", 231),
570                            fore(
571                                indent!(format!(
572                                    "get_byte_slice_of(ptr, {}, {})",
573                                    $index, $cutoff
574                                )),
575                                231,
576                            ),
577                            fore("is:", 231),
578                            format_bytes(get_byte_slice_of($ptr, $index, count), None),
579                        ]
580                        .iter()
581                        .map(|c| format!("        {c}"))
582                        .collect::<Vec<String>>()
583                        .join("\n")
584                    )
585                }
586            ]
587            .join(" ")
588        );
589
590        let index = $index;
591        let length = $length - index;
592        let actual = match RuneParts::from_raw_parts($ptr, $length)
593            .rune_at_index($index) {
594                Ok(actual) => actual.as_str().to_string(),
595                Err(error) => {
596                    panic!("{}:{} RuneParts::from_raw_parts({:#?}, {})\n{error}", file!(), line!(), $ptr, $length);
597                }
598            };
599
600        let expected = $expected.to_string();
601        assert_eq!(
602            actual,
603            expected.to_string(),
604            "{}",
605            ansi(
606                [
607                    String::new(),
608                    [
609                        fore("expected", 82),
610                        fore("rune as string", 231),
611                        fore(format!("{expected}"), 82),
612                    ]
613                    .join(" "),
614                    [
615                        fore("actual", 196),
616                        fore("rune as string", 231),
617                        fore(format!("{actual}"), 196),
618                    ]
619                    .join(" "),
620                    [
621                        fore("from slice ", 220),
622                        fore("[", 231),
623                        format!("{slice}"),
624                        fore("]", 231),
625                        [
626                            String::new(),
627                            fore(format!("index={index}"), 82),
628                            fore(format!("cutoff={cutoff}"), 202),
629                            fore(format!("length={length}"), 74),
630                        ]
631                        .join(" "),
632                    ]
633                    .join(""),
634                    fore(format!("expected cutoff={}", $cutoff), 196),
635                ]
636                .join("\n"),
637                231,
638                16
639            ),
640        );
641    }};
642}
643
644    fn format_expected_rune(c: &str) -> String {
645        use debug_et_diagnostics::color::byte_hex;
646        format!(
647            "\"{c}\" => [{}]",
648            c.as_bytes()
649                .iter()
650                .map(Clone::clone)
651                .map(byte_hex)
652                .collect::<Vec<String>>()
653                .join(", "),
654        )
655    }
656}
657
658#[cfg(test)]
659mod test_continuation_bytes_location {
660    use crate::pointer::{self};
661    use crate::{continuation_bytes_location, Result};
662    #[test]
663    fn test_continuation_byte_0x200d() -> Result<()> {
664        let (ptr, length) = pointer::from_display("πŸ‘©πŸ»β€πŸš’")?;
665
666        assert_eq!(
667            "πŸ‘©πŸ»β€πŸš’".as_bytes().to_vec(),
668            vec![
669                0xF0, 0x9F, 0x91, 0xA9, 0xF0, 0x9F, 0x8F, 0xBB, 0xE2, 0x80, 0x8D, 0xF0,
670                0x9F, 0x9A, 0x92
671            ]
672        );
673        let location = continuation_bytes_location(ptr, length, 8);
674
675        assert_eq!(location.is_some(), true);
676        let (count, ty) = location.unwrap();
677        assert_eq!(ty.len(), 4);
678        assert_eq!(ty.byte(), 0xF0);
679        assert_eq!(count, 7);
680
681        Ok(())
682    }
683    #[test]
684    fn test_continuation_byte_0xfe0f() -> Result<()> {
685        let (ptr, length) = pointer::from_display("❀️‍πŸ”₯")?;
686        assert_eq!(
687            "❀️‍πŸ”₯".as_bytes().to_vec(),
688            vec![
689                0xE2, 0x9D, 0xA4, 0xEF, 0xB8, 0x8F, 0xE2, 0x80, 0x8D, 0xF0, 0x9F, 0x94,
690                0xA5
691            ]
692        );
693        let location = continuation_bytes_location(ptr, length, 3);
694
695        assert_eq!(location.is_some(), true);
696        let (count, ty) = location.unwrap();
697        assert_eq!(ty.len(), 3);
698        assert_eq!(ty.byte(), 0xE2);
699        assert_eq!(count, 10);
700
701        Ok(())
702    }
703}
704
705#[cfg(test)]
706mod test_next_valid_cutoff {
707
708    use crate::heuristic::next_valid_cutoff;
709    use crate::pointer::{self};
710    use crate::{
711        assert_none_next_valid_cutoff, assert_some_next_valid_cutoff, Result, RuneParts,
712    };
713    #[test]
714    fn test_next_valid_cutoff_parts() -> Result<()> {
715        let (ptr, length) = pointer::from_slice("πŸ‘ŒπŸ‘ŒπŸ»πŸ‘ŒπŸΌπŸ‘ŒπŸ½πŸ‘ŒπŸΎπŸ‘ŒπŸΏ".as_bytes())?;
716        assert_some_next_valid_cutoff!(ptr, length, 44, 0, 0, "πŸ‘Œ");
717        assert_some_next_valid_cutoff!(ptr, length, 44, 1, 4, "πŸ‘ŒπŸ»");
718        assert_some_next_valid_cutoff!(ptr, length, 44, 2, 4, "πŸ‘ŒπŸ»");
719        assert_some_next_valid_cutoff!(ptr, length, 44, 3, 4, "πŸ‘ŒπŸ»");
720        assert_some_next_valid_cutoff!(ptr, length, 44, 4, 4, "πŸ‘ŒπŸ»");
721
722        assert_some_next_valid_cutoff!(ptr, length, 44, 5, 8, "🏻");
723        assert_some_next_valid_cutoff!(ptr, length, 44, 6, 8, "🏻");
724        assert_some_next_valid_cutoff!(ptr, length, 44, 7, 8, "🏻");
725        assert_some_next_valid_cutoff!(ptr, length, 44, 8, 8, "🏻");
726        assert_some_next_valid_cutoff!(ptr, length, 44, 9, 12, "πŸ‘ŒπŸΌ");
727        assert_some_next_valid_cutoff!(ptr, length, 44, 10, 12, "πŸ‘ŒπŸΌ");
728        assert_some_next_valid_cutoff!(ptr, length, 44, 11, 12, "πŸ‘ŒπŸΌ");
729        assert_some_next_valid_cutoff!(ptr, length, 44, 12, 12, "πŸ‘ŒπŸΌ");
730
731        assert_some_next_valid_cutoff!(ptr, length, 44, 13, 16, "🏼");
732        assert_some_next_valid_cutoff!(ptr, length, 44, 14, 16, "🏼");
733        assert_some_next_valid_cutoff!(ptr, length, 44, 15, 16, "🏼");
734        assert_some_next_valid_cutoff!(ptr, length, 44, 16, 16, "🏼");
735        assert_some_next_valid_cutoff!(ptr, length, 44, 17, 20, "πŸ‘ŒπŸ½");
736        assert_some_next_valid_cutoff!(ptr, length, 44, 18, 20, "πŸ‘ŒπŸ½");
737        assert_some_next_valid_cutoff!(ptr, length, 44, 19, 20, "πŸ‘ŒπŸ½");
738        assert_some_next_valid_cutoff!(ptr, length, 44, 20, 20, "πŸ‘ŒπŸ½");
739
740        assert_some_next_valid_cutoff!(ptr, length, 44, 21, 24, "🏽");
741        assert_some_next_valid_cutoff!(ptr, length, 44, 22, 24, "🏽");
742        assert_some_next_valid_cutoff!(ptr, length, 44, 23, 24, "🏽");
743        assert_some_next_valid_cutoff!(ptr, length, 44, 24, 24, "🏽");
744        assert_some_next_valid_cutoff!(ptr, length, 44, 25, 28, "πŸ‘ŒπŸΎ");
745        assert_some_next_valid_cutoff!(ptr, length, 44, 26, 28, "πŸ‘ŒπŸΎ");
746        assert_some_next_valid_cutoff!(ptr, length, 44, 27, 28, "πŸ‘ŒπŸΎ");
747        assert_some_next_valid_cutoff!(ptr, length, 44, 28, 28, "πŸ‘ŒπŸΎ");
748
749        assert_some_next_valid_cutoff!(ptr, length, 44, 29, 32, "🏾");
750        assert_some_next_valid_cutoff!(ptr, length, 44, 30, 32, "🏾");
751        assert_some_next_valid_cutoff!(ptr, length, 44, 31, 32, "🏾");
752        assert_some_next_valid_cutoff!(ptr, length, 44, 32, 32, "🏾");
753        assert_some_next_valid_cutoff!(ptr, length, 44, 33, 36, "πŸ‘ŒπŸΏ");
754        assert_some_next_valid_cutoff!(ptr, length, 44, 34, 36, "πŸ‘ŒπŸΏ");
755        assert_some_next_valid_cutoff!(ptr, length, 44, 35, 36, "πŸ‘ŒπŸΏ");
756        assert_some_next_valid_cutoff!(ptr, length, 44, 36, 36, "πŸ‘ŒπŸΏ");
757
758        assert_some_next_valid_cutoff!(ptr, length, 44, 37, 40, "🏿");
759        assert_some_next_valid_cutoff!(ptr, length, 44, 38, 40, "🏿");
760        assert_some_next_valid_cutoff!(ptr, length, 44, 39, 40, "🏿");
761        assert_some_next_valid_cutoff!(ptr, length, 44, 40, 40, "🏿");
762        assert_none_next_valid_cutoff!(ptr, length, 44, 41);
763        assert_none_next_valid_cutoff!(ptr, length, 44, 42);
764        assert_none_next_valid_cutoff!(ptr, length, 44, 43);
765        assert_none_next_valid_cutoff!(ptr, length, 44, 44);
766        assert_none_next_valid_cutoff!(ptr, length, 44, 45);
767        Ok(())
768    }
769
770    #[test]
771    fn test_next_valid_cutoff_at_first_index_single_rune() -> Result<()> {
772        let (ptr, length) = pointer::from_slice("❀️".as_bytes())?;
773        assert_some_next_valid_cutoff!(ptr, length, 6, 0, 0, "❀️");
774        Ok(())
775    }
776
777    #[test]
778    fn test_next_valid_cutoff_empty() -> Result<()> {
779        let (ptr, length) = pointer::from_slice("".as_bytes())?;
780        assert_none_next_valid_cutoff!(ptr, length, 0, 0);
781
782        Ok(())
783    }
784
785    #[test]
786    fn test_next_valid_cutoff_at_various_indexes_6_bytes() -> Result<()> {
787        let (ptr, length) = pointer::from_slice("skull☠️skull".as_bytes())?;
788        assert_some_next_valid_cutoff!(ptr, length, 16, 0, 0, "s");
789        assert_some_next_valid_cutoff!(ptr, length, 16, 4, 4, "l");
790        assert_some_next_valid_cutoff!(ptr, length, 16, 5, 5, "☠️");
791        Ok(())
792    }
793
794    #[test]
795    fn test_next_valid_cutoff_at_various_indexes_4_bytes() -> Result<()> {
796        let (ptr, length) = pointer::from_slice("smileyπŸ˜€smiley".as_bytes())?;
797        assert_some_next_valid_cutoff!(ptr, length, 16, 5, 5, "y");
798        assert_some_next_valid_cutoff!(ptr, length, 16, 6, 6, "πŸ˜€");
799        Ok(())
800    }
801
802    #[test]
803    fn test_next_valid_cutoff_at_various_indexes_ascii() -> Result<()> {
804        let (ptr, length) = pointer::from_slice("abcdefghijklmnopqrstu".as_bytes())?;
805        assert_some_next_valid_cutoff!(ptr, length, 21, 7, 7, "h");
806
807        Ok(())
808    }
809
810    #[test]
811    fn test_next_valid_cutoff_at_various_indexes_non_ascii() -> Result<()> {
812        let (ptr, length) = pointer::from_slice("falcΓ£oπŸ¦…".as_bytes())?;
813        assert_some_next_valid_cutoff!(ptr, length, 11, 4, 4, "Γ£");
814        assert_some_next_valid_cutoff!(ptr, length, 11, 5, 6, "o");
815        assert_some_next_valid_cutoff!(ptr, length, 11, 6, 6, "o");
816        assert_some_next_valid_cutoff!(ptr, length, 11, 7, 7, "πŸ¦…");
817        assert_none_next_valid_cutoff!(ptr, length, 11, 8);
818        Ok(())
819    }
820
821    #[test]
822    fn test_next_valid_cutoff_at_first_index() -> Result<()> {
823        let (ptr, length) = pointer::from_slice("β€οΈπŸ¦…".as_bytes())?;
824        assert_some_next_valid_cutoff!(ptr, length, 10, 0, 0, "❀️");
825        assert_some_next_valid_cutoff!(ptr, length, 10, 1, 6, "πŸ¦…");
826        assert_some_next_valid_cutoff!(ptr, length, 10, 2, 6, "πŸ¦…");
827        assert_some_next_valid_cutoff!(ptr, length, 10, 3, 6, "πŸ¦…");
828        assert_some_next_valid_cutoff!(ptr, length, 10, 4, 6, "πŸ¦…");
829        assert_some_next_valid_cutoff!(ptr, length, 10, 5, 6, "πŸ¦…");
830        assert_some_next_valid_cutoff!(ptr, length, 10, 6, 6, "πŸ¦…");
831        assert_none_next_valid_cutoff!(ptr, length, 10, 7);
832
833        Ok(())
834    }
835
836    #[test]
837    fn test_next_valid_cutoff_at_various_indexes_94_bytes() -> Result<()> {
838        let (ptr, length) = pointer::from_slice("πŸ‘©πŸ»β€πŸš’πŸ‘ŒπŸΏπŸ§‘πŸ½β€πŸš’πŸ‘¨β€πŸš’πŸŒΆοΈπŸŽΉπŸ’”πŸ”₯❀️‍πŸ”₯β€οΈβ€πŸ©Ή".as_bytes())?;
839        assert_some_next_valid_cutoff!(ptr, length, 94, 0, 0, "πŸ‘©πŸ»β€πŸš’");
840        assert_some_next_valid_cutoff!(ptr, length, 94, 1, 4, "🏻\u{200d}πŸš’");
841        assert_some_next_valid_cutoff!(ptr, length, 94, 2, 4, "🏻\u{200d}πŸš’");
842        assert_some_next_valid_cutoff!(ptr, length, 94, 3, 4, "🏻\u{200d}πŸš’");
843        assert_some_next_valid_cutoff!(ptr, length, 94, 4, 4, "🏻\u{200d}πŸš’");
844        assert_some_next_valid_cutoff!(ptr, length, 94, 5, 15, "πŸ‘ŒπŸΏ");
845        assert_some_next_valid_cutoff!(ptr, length, 94, 15, 15, "πŸ‘ŒπŸΏ");
846        assert_some_next_valid_cutoff!(ptr, length, 94, 16, 19, "🏿");
847        assert_some_next_valid_cutoff!(ptr, length, 94, 17, 19, "🏿");
848        assert_some_next_valid_cutoff!(ptr, length, 94, 18, 19, "🏿");
849        assert_some_next_valid_cutoff!(ptr, length, 94, 19, 19, "🏿");
850        assert_some_next_valid_cutoff!(ptr, length, 94, 20, 23, "πŸ§‘πŸ½β€πŸš’");
851        assert_some_next_valid_cutoff!(ptr, length, 94, 21, 23, "πŸ§‘πŸ½β€πŸš’");
852        assert_some_next_valid_cutoff!(ptr, length, 94, 22, 23, "πŸ§‘πŸ½β€πŸš’");
853        assert_some_next_valid_cutoff!(ptr, length, 94, 23, 23, "πŸ§‘πŸ½β€πŸš’");
854
855        assert_some_next_valid_cutoff!(ptr, length, 94, 24, 27, "🏽\u{200d}πŸš’");
856        assert_some_next_valid_cutoff!(ptr, length, 94, 25, 27, "🏽\u{200d}πŸš’");
857        assert_some_next_valid_cutoff!(ptr, length, 94, 26, 27, "🏽\u{200d}πŸš’");
858        assert_some_next_valid_cutoff!(ptr, length, 94, 27, 27, "🏽\u{200d}πŸš’");
859        assert_some_next_valid_cutoff!(ptr, length, 94, 28, 38, "πŸ‘¨\u{200d}πŸš’");
860        assert_some_next_valid_cutoff!(ptr, length, 94, 29, 38, "πŸ‘¨\u{200d}πŸš’");
861        assert_some_next_valid_cutoff!(ptr, length, 94, 30, 38, "πŸ‘¨\u{200d}πŸš’");
862        assert_some_next_valid_cutoff!(ptr, length, 94, 31, 38, "πŸ‘¨\u{200d}πŸš’");
863        assert_some_next_valid_cutoff!(ptr, length, 94, 32, 34, "πŸš’πŸ‘¨\u{200d}πŸš’");
864        assert_some_next_valid_cutoff!(ptr, length, 94, 33, 34, "πŸš’πŸ‘¨\u{200d}πŸš’");
865        assert_some_next_valid_cutoff!(ptr, length, 94, 34, 34, "πŸš’πŸ‘¨\u{200d}πŸš’");
866        assert_some_next_valid_cutoff!(ptr, length, 94, 35, 38, "πŸ‘¨\u{200d}πŸš’");
867        assert_some_next_valid_cutoff!(ptr, length, 94, 36, 38, "πŸ‘¨\u{200d}πŸš’");
868        assert_some_next_valid_cutoff!(ptr, length, 94, 37, 38, "πŸ‘¨\u{200d}πŸš’");
869        assert_some_next_valid_cutoff!(ptr, length, 94, 38, 38, "πŸ‘¨β€πŸš’");
870        assert_some_next_valid_cutoff!(ptr, length, 94, 39, 49, "🌢️");
871        assert_some_next_valid_cutoff!(ptr, length, 94, 40, 49, "🌢️");
872        assert_some_next_valid_cutoff!(ptr, length, 94, 41, 49, "🌢️");
873        assert_some_next_valid_cutoff!(ptr, length, 94, 42, 49, "🌢️");
874        assert_some_next_valid_cutoff!(ptr, length, 94, 43, 45, "πŸš’πŸŒΆοΈ");
875        assert_some_next_valid_cutoff!(ptr, length, 94, 44, 45, "πŸš’πŸŒΆοΈ");
876        assert_some_next_valid_cutoff!(ptr, length, 94, 45, 45, "πŸš’πŸŒΆοΈ");
877        assert_some_next_valid_cutoff!(ptr, length, 94, 46, 49, "🌢️");
878        assert_some_next_valid_cutoff!(ptr, length, 94, 47, 49, "🌢️");
879        assert_some_next_valid_cutoff!(ptr, length, 94, 48, 49, "🌢️");
880        assert_some_next_valid_cutoff!(ptr, length, 94, 49, 49, "🌢️");
881        assert_some_next_valid_cutoff!(ptr, length, 94, 49, 49, "🌢️");
882        assert_some_next_valid_cutoff!(ptr, length, 94, 50, 56, "🎹");
883        assert_some_next_valid_cutoff!(ptr, length, 94, 51, 56, "🎹");
884        assert_some_next_valid_cutoff!(ptr, length, 94, 52, 56, "🎹");
885        assert_some_next_valid_cutoff!(ptr, length, 94, 53, 56, "🎹");
886        assert_some_next_valid_cutoff!(ptr, length, 94, 54, 56, "🎹");
887        assert_some_next_valid_cutoff!(ptr, length, 94, 55, 56, "🎹");
888        assert_some_next_valid_cutoff!(ptr, length, 94, 56, 56, "🎹");
889        assert_some_next_valid_cutoff!(ptr, length, 94, 57, 60, "πŸ’”");
890        assert_some_next_valid_cutoff!(ptr, length, 94, 58, 60, "πŸ’”");
891        assert_some_next_valid_cutoff!(ptr, length, 94, 59, 60, "πŸ’”");
892        assert_some_next_valid_cutoff!(ptr, length, 94, 60, 60, "πŸ’”");
893        assert_some_next_valid_cutoff!(ptr, length, 94, 61, 64, "πŸ”₯");
894        assert_some_next_valid_cutoff!(ptr, length, 94, 62, 64, "πŸ”₯");
895        assert_some_next_valid_cutoff!(ptr, length, 94, 63, 64, "πŸ”₯");
896        assert_some_next_valid_cutoff!(ptr, length, 94, 64, 64, "πŸ”₯");
897        assert_some_next_valid_cutoff!(ptr, length, 94, 65, 68, "❀️‍πŸ”₯");
898        assert_some_next_valid_cutoff!(ptr, length, 94, 66, 68, "❀️‍πŸ”₯");
899        assert_some_next_valid_cutoff!(ptr, length, 94, 67, 68, "❀️‍πŸ”₯");
900        assert_some_next_valid_cutoff!(ptr, length, 94, 68, 68, "❀️‍πŸ”₯");
901        assert_some_next_valid_cutoff!(ptr, length, 94, 69, 81, "β€οΈβ€πŸ©Ή");
902        assert_some_next_valid_cutoff!(ptr, length, 94, 70, 81, "β€οΈβ€πŸ©Ή");
903        assert_some_next_valid_cutoff!(ptr, length, 94, 71, 81, "β€οΈβ€πŸ©Ή");
904        assert_some_next_valid_cutoff!(ptr, length, 94, 72, 81, "β€οΈβ€πŸ©Ή");
905        assert_some_next_valid_cutoff!(ptr, length, 94, 73, 81, "β€οΈβ€πŸ©Ή");
906        assert_some_next_valid_cutoff!(ptr, length, 94, 74, 81, "β€οΈβ€πŸ©Ή");
907        assert_some_next_valid_cutoff!(ptr, length, 94, 75, 77, "πŸ”₯");
908        assert_some_next_valid_cutoff!(ptr, length, 94, 76, 77, "πŸ”₯");
909        assert_some_next_valid_cutoff!(ptr, length, 94, 77, 77, "πŸ”₯");
910        assert_some_next_valid_cutoff!(ptr, length, 94, 78, 81, "β€οΈβ€πŸ©Ή");
911        assert_some_next_valid_cutoff!(ptr, length, 94, 79, 81, "β€οΈβ€πŸ©Ή");
912        assert_some_next_valid_cutoff!(ptr, length, 94, 80, 81, "β€οΈβ€πŸ©Ή");
913        assert_some_next_valid_cutoff!(ptr, length, 94, 81, 81, "β€οΈβ€πŸ©Ή");
914        assert_none_next_valid_cutoff!(ptr, length, 94, 82);
915        assert_none_next_valid_cutoff!(ptr, length, 94, 83);
916        assert_none_next_valid_cutoff!(ptr, length, 94, 84);
917        assert_none_next_valid_cutoff!(ptr, length, 94, 85);
918        assert_none_next_valid_cutoff!(ptr, length, 94, 86);
919        assert_none_next_valid_cutoff!(ptr, length, 94, 87);
920        assert_some_next_valid_cutoff!(ptr, length, 94, 88, 90, "🩹");
921        assert_some_next_valid_cutoff!(ptr, length, 94, 89, 90, "🩹");
922        assert_some_next_valid_cutoff!(ptr, length, 94, 90, 90, "🩹");
923        assert_none_next_valid_cutoff!(ptr, length, 94, 91);
924        assert_none_next_valid_cutoff!(ptr, length, 94, 92);
925        assert_none_next_valid_cutoff!(ptr, length, 94, 93);
926        assert_none_next_valid_cutoff!(ptr, length, 94, 94);
927
928        Ok(())
929    }
930
931    #[macro_export]
932    macro_rules! assert_some_next_valid_cutoff {
933    (
934        $ptr:expr,
935        $length:expr,
936        $expected_length:literal,
937        $invalid_index:literal,
938        $expected_valid_index:literal,
939        $expected_rune_str:literal
940        $(,)?
941    ) => {{
942        // debug_et_diagnostics::step!(fg=line, format!("expecting next_valid_cutoff from invalid index {} to be {} matching rune \"{}\"", $invalid_index, $expected_valid_index, $expected_rune_str));
943
944        assert_eq!($length, $expected_length, "expected length to be {} rather than {}", $expected_length, $length);
945        let result = next_valid_cutoff($ptr, $length, $invalid_index);
946        assert!(result.is_some(), "expected next_valid_cutoff at {} to not be None", $invalid_index);
947        let actual = result.unwrap();
948        assert_eq!(actual, $expected_valid_index, "expected next_valid_cutoff to be {} rather than {}", $expected_valid_index, actual);
949        let parts = RuneParts::from_raw_parts($ptr, $length);
950        let result = parts.rune_at_index(actual);
951        assert!(result.is_ok(), "expected valid Rune at index to be {} but got error: {}", $expected_valid_index, result.err().map(|err|err.to_string()).unwrap_or_default());
952        let rune = result.unwrap();
953        assert_eq!(rune.as_str(), $expected_rune_str,
954                   "expected rune at index {} to match \"{}\" rather than \"{}\"",
955                   actual, $expected_rune_str, rune.as_str());
956    }};
957}
958
959    #[macro_export]
960    macro_rules! assert_none_next_valid_cutoff {
961        (
962            $ptr:expr,
963            $length:expr,
964            $expected_length:literal,
965            $invalid_index:literal $(,)?
966        ) => {{
967            // debug_et_diagnostics::step!(fg = (line!() as u8),format!("expecting next_valid_cutoff from invalid index {} to be None",$invalid_index));
968            assert_eq!(
969                $length, $expected_length,
970                "expected length to be {} rather than {}",
971                $expected_length, $length
972            );
973            let result = next_valid_cutoff($ptr, $length, $invalid_index);
974            assert!(
975                result.is_none(),
976                "expected next_valid_cutoff at {} to not be None but is actually {:#?}",
977                $invalid_index,
978                result
979            );
980        }};
981    }
982}
983
984#[cfg(test)]
985mod test_previous_valid_cutoff {
986    use crate::heuristic::previous_valid_cutoff;
987    use crate::pointer::{self};
988    use crate::{
989        assert_none_previous_valid_cutoff, assert_some_previous_valid_cutoff, Result,
990        RuneParts,
991    };
992
993    #[test]
994    fn test_previous_valid_cutoff_parts() -> Result<()> {
995        let (ptr, length) = pointer::from_slice("πŸ‘ŒπŸ‘ŒπŸ»πŸ‘ŒπŸΌπŸ‘ŒπŸ½πŸ‘ŒπŸΎπŸ‘ŒπŸΏ".as_bytes())?;
996        assert_some_previous_valid_cutoff!(ptr, length, 44, 0, 0, "πŸ‘Œ");
997        assert_some_previous_valid_cutoff!(ptr, length, 44, 1, 0, "πŸ‘Œ");
998        assert_some_previous_valid_cutoff!(ptr, length, 44, 5, 4, "πŸ‘ŒπŸ»");
999        assert_some_previous_valid_cutoff!(ptr, length, 44, 13, 12, "πŸ‘ŒπŸΌ");
1000        assert_some_previous_valid_cutoff!(ptr, length, 44, 21, 20, "πŸ‘ŒπŸ½");
1001        assert_some_previous_valid_cutoff!(ptr, length, 44, 29, 28, "πŸ‘ŒπŸΎ");
1002        assert_some_previous_valid_cutoff!(ptr, length, 44, 37, 36, "πŸ‘ŒπŸΏ");
1003        Ok(())
1004    }
1005    #[test]
1006    fn test_previous_valid_cutoff_at_first_index_single_rune() -> Result<()> {
1007        let (ptr, length) = pointer::from_slice("❀️".as_bytes())?;
1008        assert_some_previous_valid_cutoff!(ptr, length, 6, 0, 0, "❀️");
1009        Ok(())
1010    }
1011
1012    #[test]
1013    fn test_previous_valid_cutoff_empty() -> Result<()> {
1014        let (ptr, length) = pointer::from_slice("".as_bytes())?;
1015        assert_none_previous_valid_cutoff!(ptr, length, 0, 0);
1016
1017        Ok(())
1018    }
1019
1020    #[test]
1021    fn test_previous_valid_cutoff_at_various_indexes_6_bytes() -> Result<()> {
1022        let (ptr, length) = pointer::from_slice("skull☠️skull".as_bytes())?;
1023        assert_none_previous_valid_cutoff!(ptr, length, 16, 0);
1024        assert_none_previous_valid_cutoff!(ptr, length, 16, 1);
1025        assert_none_previous_valid_cutoff!(ptr, length, 16, 2);
1026        assert_none_previous_valid_cutoff!(ptr, length, 16, 3);
1027        assert_none_previous_valid_cutoff!(ptr, length, 16, 4);
1028        assert_some_previous_valid_cutoff!(ptr, length, 16, 5, 5, "☠️");
1029        assert_some_previous_valid_cutoff!(ptr, length, 16, 6, 5, "☠️");
1030        assert_some_previous_valid_cutoff!(ptr, length, 16, 7, 5, "☠️");
1031        assert_some_previous_valid_cutoff!(ptr, length, 16, 8, 5, "☠️");
1032        assert_some_previous_valid_cutoff!(ptr, length, 16, 9, 5, "☠️");
1033        assert_some_previous_valid_cutoff!(ptr, length, 16, 10, 5, "☠️");
1034        assert_some_previous_valid_cutoff!(ptr, length, 16, 11, 5, "☠️");
1035        assert_some_previous_valid_cutoff!(ptr, length, 16, 12, 5, "☠️");
1036        assert_some_previous_valid_cutoff!(ptr, length, 16, 13, 5, "☠️");
1037        assert_some_previous_valid_cutoff!(ptr, length, 16, 14, 5, "☠️");
1038        assert_some_previous_valid_cutoff!(ptr, length, 16, 15, 5, "☠️");
1039        assert_some_previous_valid_cutoff!(ptr, length, 16, 16, 5, "☠️");
1040        Ok(())
1041    }
1042
1043    #[test]
1044    fn test_previous_valid_cutoff_at_various_indexes_4_bytes() -> Result<()> {
1045        let (ptr, length) = pointer::from_slice("smileyπŸ˜€smiley".as_bytes())?;
1046        assert_none_previous_valid_cutoff!(ptr, length, 16, 0);
1047        assert_none_previous_valid_cutoff!(ptr, length, 16, 1);
1048        assert_none_previous_valid_cutoff!(ptr, length, 16, 2);
1049        assert_none_previous_valid_cutoff!(ptr, length, 16, 3);
1050        assert_none_previous_valid_cutoff!(ptr, length, 16, 4);
1051        assert_none_previous_valid_cutoff!(ptr, length, 16, 5);
1052        assert_some_previous_valid_cutoff!(ptr, length, 16, 6, 6, "πŸ˜€");
1053        assert_some_previous_valid_cutoff!(ptr, length, 16, 7, 6, "πŸ˜€");
1054        assert_some_previous_valid_cutoff!(ptr, length, 16, 8, 6, "πŸ˜€");
1055        assert_some_previous_valid_cutoff!(ptr, length, 16, 9, 6, "πŸ˜€");
1056        assert_some_previous_valid_cutoff!(ptr, length, 16, 10, 6, "πŸ˜€");
1057        assert_some_previous_valid_cutoff!(ptr, length, 16, 11, 6, "πŸ˜€");
1058        assert_some_previous_valid_cutoff!(ptr, length, 16, 12, 6, "πŸ˜€");
1059        assert_some_previous_valid_cutoff!(ptr, length, 16, 13, 6, "πŸ˜€");
1060        assert_some_previous_valid_cutoff!(ptr, length, 16, 14, 6, "πŸ˜€");
1061        assert_some_previous_valid_cutoff!(ptr, length, 16, 15, 6, "πŸ˜€");
1062        assert_some_previous_valid_cutoff!(ptr, length, 16, 16, 6, "πŸ˜€");
1063        Ok(())
1064    }
1065
1066    #[test]
1067    fn test_previous_valid_cutoff_at_various_indexes_ascii() -> Result<()> {
1068        let (ptr, length) = pointer::from_slice("abcdefghijklmnopqrstu".as_bytes())?;
1069        assert_none_previous_valid_cutoff!(ptr, length, 21, 7);
1070
1071        Ok(())
1072    }
1073
1074    #[test]
1075    fn test_previous_valid_cutoff_at_various_indexes_non_ascii() -> Result<()> {
1076        let (ptr, length) = pointer::from_slice("falcΓ£oπŸ¦…".as_bytes())?;
1077        assert_some_previous_valid_cutoff!(ptr, length, 11, 4, 4, "Γ£");
1078        assert_some_previous_valid_cutoff!(ptr, length, 11, 5, 4, "Γ£");
1079        assert_some_previous_valid_cutoff!(ptr, length, 11, 6, 4, "Γ£");
1080        assert_some_previous_valid_cutoff!(ptr, length, 11, 7, 7, "πŸ¦…");
1081        assert_some_previous_valid_cutoff!(ptr, length, 11, 8, 7, "πŸ¦…");
1082        assert_some_previous_valid_cutoff!(ptr, length, 11, 9, 7, "πŸ¦…");
1083        assert_some_previous_valid_cutoff!(ptr, length, 11, 10, 7, "πŸ¦…");
1084        assert_some_previous_valid_cutoff!(ptr, length, 11, 11, 7, "πŸ¦…");
1085        assert_some_previous_valid_cutoff!(ptr, length, 11, 12, 7, "πŸ¦…");
1086        assert_some_previous_valid_cutoff!(ptr, length, 11, 13, 7, "πŸ¦…");
1087        assert_some_previous_valid_cutoff!(ptr, length, 11, 14, 7, "πŸ¦…");
1088        assert_some_previous_valid_cutoff!(ptr, length, 11, 15, 7, "πŸ¦…");
1089        Ok(())
1090    }
1091
1092    #[test]
1093    fn test_previous_valid_cutoff_at_first_index() -> Result<()> {
1094        let (ptr, length) = pointer::from_display("β€οΈπŸ¦…")?;
1095        assert_some_previous_valid_cutoff!(ptr, length, 10, 0, 0, "❀️");
1096        assert_some_previous_valid_cutoff!(ptr, length, 10, 1, 0, "❀️");
1097        assert_some_previous_valid_cutoff!(ptr, length, 10, 2, 0, "❀️");
1098        assert_some_previous_valid_cutoff!(ptr, length, 10, 3, 0, "❀️");
1099        assert_some_previous_valid_cutoff!(ptr, length, 10, 4, 0, "❀️");
1100        assert_some_previous_valid_cutoff!(ptr, length, 10, 5, 0, "❀️");
1101        assert_some_previous_valid_cutoff!(ptr, length, 10, 6, 6, "πŸ¦…");
1102        assert_some_previous_valid_cutoff!(ptr, length, 10, 7, 6, "πŸ¦…");
1103        assert_some_previous_valid_cutoff!(ptr, length, 10, 8, 6, "πŸ¦…");
1104        assert_some_previous_valid_cutoff!(ptr, length, 10, 9, 6, "πŸ¦…");
1105        assert_some_previous_valid_cutoff!(ptr, length, 10, 10, 6, "πŸ¦…");
1106        assert_some_previous_valid_cutoff!(ptr, length, 10, 11, 6, "πŸ¦…");
1107        assert_some_previous_valid_cutoff!(ptr, length, 10, 12, 6, "πŸ¦…");
1108        assert_some_previous_valid_cutoff!(ptr, length, 10, 13, 6, "πŸ¦…");
1109        assert_some_previous_valid_cutoff!(ptr, length, 10, 14, 6, "πŸ¦…");
1110        assert_some_previous_valid_cutoff!(ptr, length, 10, 15, 6, "πŸ¦…");
1111        Ok(())
1112    }
1113
1114    #[test]
1115    fn test_previous_valid_cutoff_at_various_indexes_94_bytes() -> Result<()> {
1116        let (ptr, length) = pointer::from_display("πŸ‘©πŸ»β€πŸš’πŸ‘ŒπŸΏπŸ§‘πŸ½β€πŸš’πŸ‘¨β€πŸš’πŸŒΆοΈπŸŽΉπŸ’”πŸ”₯❀️‍πŸ”₯β€οΈβ€πŸ©Ή")?;
1117        assert_some_previous_valid_cutoff!(ptr, length, 94, 0, 0, "πŸ‘©πŸ»β€πŸš’");
1118        assert_some_previous_valid_cutoff!(ptr, length, 94, 1, 0, "πŸ‘©πŸ»β€πŸš’");
1119        assert_some_previous_valid_cutoff!(ptr, length, 94, 2, 0, "πŸ‘©πŸ»β€πŸš’");
1120        assert_some_previous_valid_cutoff!(ptr, length, 94, 3, 0, "πŸ‘©πŸ»β€πŸš’");
1121        assert_some_previous_valid_cutoff!(ptr, length, 94, 4, 4, "🏻\u{200d}πŸš’");
1122        assert_some_previous_valid_cutoff!(ptr, length, 94, 5, 4, "🏻\u{200d}πŸš’");
1123        assert_some_previous_valid_cutoff!(ptr, length, 94, 6, 4, "🏻\u{200d}πŸš’");
1124        assert_some_previous_valid_cutoff!(ptr, length, 94, 7, 4, "🏻\u{200d}πŸš’");
1125        assert_some_previous_valid_cutoff!(ptr, length, 94, 8, 4, "🏻\u{200d}πŸš’");
1126        assert_some_previous_valid_cutoff!(ptr, length, 94, 9, 4, "🏻\u{200d}πŸš’");
1127        assert_some_previous_valid_cutoff!(ptr, length, 94, 10, 4, "🏻\u{200d}πŸš’");
1128        assert_some_previous_valid_cutoff!(ptr, length, 94, 11, 11, "πŸš’");
1129        assert_some_previous_valid_cutoff!(ptr, length, 94, 12, 11, "πŸš’");
1130        assert_some_previous_valid_cutoff!(ptr, length, 94, 13, 11, "πŸš’");
1131        assert_some_previous_valid_cutoff!(ptr, length, 94, 14, 11, "πŸš’");
1132        assert_some_previous_valid_cutoff!(ptr, length, 94, 15, 15, "πŸ‘ŒπŸΏ");
1133        assert_some_previous_valid_cutoff!(ptr, length, 94, 16, 15, "πŸ‘ŒπŸΏ");
1134        assert_some_previous_valid_cutoff!(ptr, length, 94, 17, 15, "πŸ‘ŒπŸΏ");
1135        assert_some_previous_valid_cutoff!(ptr, length, 94, 18, 15, "πŸ‘ŒπŸΏ");
1136        assert_some_previous_valid_cutoff!(ptr, length, 94, 19, 19, "🏿");
1137        assert_some_previous_valid_cutoff!(ptr, length, 94, 20, 19, "🏿");
1138        assert_some_previous_valid_cutoff!(ptr, length, 94, 21, 19, "🏿");
1139        assert_some_previous_valid_cutoff!(ptr, length, 94, 22, 19, "🏿");
1140        assert_some_previous_valid_cutoff!(ptr, length, 94, 23, 23, "πŸ§‘πŸ½β€πŸš’");
1141        assert_some_previous_valid_cutoff!(ptr, length, 94, 24, 23, "πŸ§‘πŸ½β€πŸš’");
1142        assert_some_previous_valid_cutoff!(ptr, length, 94, 25, 23, "πŸ§‘πŸ½β€πŸš’");
1143        assert_some_previous_valid_cutoff!(ptr, length, 94, 26, 23, "πŸ§‘πŸ½β€πŸš’");
1144        assert_some_previous_valid_cutoff!(ptr, length, 94, 27, 27, "🏽\u{200d}πŸš’");
1145        assert_some_previous_valid_cutoff!(ptr, length, 94, 28, 27, "🏽\u{200d}πŸš’");
1146        assert_some_previous_valid_cutoff!(ptr, length, 94, 29, 27, "🏽\u{200d}πŸš’");
1147        assert_some_previous_valid_cutoff!(ptr, length, 94, 30, 27, "🏽\u{200d}πŸš’");
1148        assert_some_previous_valid_cutoff!(ptr, length, 94, 31, 27, "🏽\u{200d}πŸš’");
1149        assert_some_previous_valid_cutoff!(ptr, length, 94, 32, 27, "🏽\u{200d}πŸš’");
1150        assert_some_previous_valid_cutoff!(ptr, length, 94, 33, 27, "🏽\u{200d}πŸš’");
1151        assert_some_previous_valid_cutoff!(ptr, length, 94, 34, 34, "πŸš’πŸ‘¨\u{200d}πŸš’");
1152        assert_some_previous_valid_cutoff!(ptr, length, 94, 35, 34, "πŸš’πŸ‘¨\u{200d}πŸš’");
1153        assert_some_previous_valid_cutoff!(ptr, length, 94, 36, 34, "πŸš’πŸ‘¨\u{200d}πŸš’");
1154        assert_some_previous_valid_cutoff!(ptr, length, 94, 36, 34, "πŸš’πŸ‘¨\u{200d}πŸš’");
1155        assert_some_previous_valid_cutoff!(ptr, length, 94, 37, 34, "πŸš’πŸ‘¨\u{200d}πŸš’");
1156        assert_some_previous_valid_cutoff!(ptr, length, 94, 38, 38, "πŸ‘¨\u{200d}πŸš’");
1157        assert_some_previous_valid_cutoff!(ptr, length, 94, 39, 38, "πŸ‘¨\u{200d}πŸš’");
1158        assert_some_previous_valid_cutoff!(ptr, length, 94, 40, 38, "πŸ‘¨\u{200d}πŸš’");
1159        assert_some_previous_valid_cutoff!(ptr, length, 94, 41, 38, "πŸ‘¨\u{200d}πŸš’");
1160        assert_some_previous_valid_cutoff!(ptr, length, 94, 42, 38, "πŸ‘¨\u{200d}πŸš’");
1161        assert_some_previous_valid_cutoff!(ptr, length, 94, 43, 38, "πŸ‘¨\u{200d}πŸš’");
1162        assert_some_previous_valid_cutoff!(ptr, length, 94, 44, 38, "πŸ‘¨\u{200d}πŸš’");
1163        assert_some_previous_valid_cutoff!(ptr, length, 94, 45, 45, "πŸš’πŸŒΆ\u{fe0f}");
1164        assert_some_previous_valid_cutoff!(ptr, length, 94, 46, 45, "πŸš’πŸŒΆοΈ");
1165        assert_some_previous_valid_cutoff!(ptr, length, 94, 47, 45, "πŸš’πŸŒΆοΈ");
1166        assert_some_previous_valid_cutoff!(ptr, length, 94, 46, 45, "πŸš’πŸŒΆοΈ");
1167        assert_some_previous_valid_cutoff!(ptr, length, 94, 47, 45, "πŸš’πŸŒΆοΈ");
1168        assert_some_previous_valid_cutoff!(ptr, length, 94, 48, 45, "πŸš’πŸŒΆοΈ");
1169        assert_some_previous_valid_cutoff!(ptr, length, 94, 49, 49, "🌢️");
1170        assert_some_previous_valid_cutoff!(ptr, length, 94, 50, 49, "🌢️");
1171        assert_some_previous_valid_cutoff!(ptr, length, 94, 51, 49, "🌢️");
1172        assert_some_previous_valid_cutoff!(ptr, length, 94, 52, 49, "🌢️");
1173        assert_some_previous_valid_cutoff!(ptr, length, 94, 53, 49, "🌢️");
1174        assert_some_previous_valid_cutoff!(ptr, length, 94, 54, 49, "🌢️");
1175        assert_some_previous_valid_cutoff!(ptr, length, 94, 55, 49, "🌢️");
1176        assert_some_previous_valid_cutoff!(ptr, length, 94, 56, 56, "🎹");
1177        assert_some_previous_valid_cutoff!(ptr, length, 94, 57, 56, "🎹");
1178        assert_some_previous_valid_cutoff!(ptr, length, 94, 58, 56, "🎹");
1179        assert_some_previous_valid_cutoff!(ptr, length, 94, 59, 56, "🎹");
1180        assert_some_previous_valid_cutoff!(ptr, length, 94, 60, 60, "πŸ’”");
1181        assert_some_previous_valid_cutoff!(ptr, length, 94, 61, 60, "πŸ’”");
1182        assert_some_previous_valid_cutoff!(ptr, length, 94, 62, 60, "πŸ’”");
1183        assert_some_previous_valid_cutoff!(ptr, length, 94, 63, 60, "πŸ’”");
1184        assert_some_previous_valid_cutoff!(ptr, length, 94, 64, 64, "πŸ”₯");
1185        assert_some_previous_valid_cutoff!(ptr, length, 94, 65, 64, "πŸ”₯");
1186        assert_some_previous_valid_cutoff!(ptr, length, 94, 66, 64, "πŸ”₯");
1187        assert_some_previous_valid_cutoff!(ptr, length, 94, 67, 64, "πŸ”₯");
1188        assert_some_previous_valid_cutoff!(ptr, length, 94, 68, 68, "❀️‍πŸ”₯");
1189        assert_some_previous_valid_cutoff!(ptr, length, 94, 69, 68, "❀️‍πŸ”₯");
1190        assert_some_previous_valid_cutoff!(ptr, length, 94, 70, 68, "❀️‍πŸ”₯");
1191        assert_some_previous_valid_cutoff!(ptr, length, 94, 71, 68, "❀️‍πŸ”₯");
1192        assert_some_previous_valid_cutoff!(ptr, length, 94, 72, 68, "❀️‍πŸ”₯");
1193        assert_some_previous_valid_cutoff!(ptr, length, 94, 73, 68, "❀️‍πŸ”₯");
1194        assert_some_previous_valid_cutoff!(ptr, length, 94, 74, 68, "❀️‍πŸ”₯");
1195        assert_some_previous_valid_cutoff!(ptr, length, 94, 75, 68, "❀️‍πŸ”₯");
1196        assert_some_previous_valid_cutoff!(ptr, length, 94, 76, 68, "❀️‍πŸ”₯");
1197        assert_some_previous_valid_cutoff!(ptr, length, 94, 77, 77, "πŸ”₯");
1198        assert_some_previous_valid_cutoff!(ptr, length, 94, 78, 77, "πŸ”₯");
1199        assert_some_previous_valid_cutoff!(ptr, length, 94, 79, 77, "πŸ”₯");
1200        assert_some_previous_valid_cutoff!(ptr, length, 94, 80, 77, "πŸ”₯");
1201        assert_some_previous_valid_cutoff!(ptr, length, 94, 81, 81, "β€οΈβ€πŸ©Ή");
1202        assert_some_previous_valid_cutoff!(ptr, length, 94, 82, 81, "β€οΈβ€πŸ©Ή");
1203        assert_some_previous_valid_cutoff!(ptr, length, 94, 83, 81, "β€οΈβ€πŸ©Ή");
1204        assert_some_previous_valid_cutoff!(ptr, length, 94, 84, 81, "β€οΈβ€πŸ©Ή");
1205        assert_some_previous_valid_cutoff!(ptr, length, 94, 85, 81, "β€οΈβ€πŸ©Ή");
1206        assert_some_previous_valid_cutoff!(ptr, length, 94, 86, 81, "β€οΈβ€πŸ©Ή");
1207        assert_some_previous_valid_cutoff!(ptr, length, 94, 87, 81, "β€οΈβ€πŸ©Ή");
1208        assert_some_previous_valid_cutoff!(ptr, length, 94, 88, 81, "β€οΈβ€πŸ©Ή");
1209        assert_some_previous_valid_cutoff!(ptr, length, 94, 89, 81, "β€οΈβ€πŸ©Ή");
1210        assert_some_previous_valid_cutoff!(ptr, length, 94, 90, 90, "🩹");
1211        assert_some_previous_valid_cutoff!(ptr, length, 94, 91, 90, "🩹");
1212        assert_some_previous_valid_cutoff!(ptr, length, 94, 92, 90, "🩹");
1213        assert_some_previous_valid_cutoff!(ptr, length, 94, 93, 90, "🩹");
1214        assert_some_previous_valid_cutoff!(ptr, length, 94, 94, 90, "🩹");
1215        Ok(())
1216    }
1217
1218    #[macro_export]
1219    macro_rules! assert_some_previous_valid_cutoff {
1220    (
1221        $ptr:expr,
1222        $length:expr,
1223        $expected_length:literal,
1224        $invalid_index:literal,
1225        $expected_valid_index:literal,
1226        $expected_rune_str:literal
1227        $(,)?
1228    ) => {{
1229
1230
1231        // debug_et_diagnostics::step!(fg=(line!() as u8), format!("expecting previous_valid_cutoff from invalid index {} to be {} matching rune \"{}\"", $invalid_index, $expected_valid_index, $expected_rune_str));
1232
1233        assert_eq!($length, $expected_length, "expected length to be {} rather than {}", $expected_length, $length);
1234        let result = previous_valid_cutoff($ptr, $length, $invalid_index);
1235        assert!(result.is_some(), "expected previous_valid_cutoff at {} to not be None", $invalid_index);
1236        let actual = result.unwrap();
1237        assert_eq!(actual, $expected_valid_index, "expected previous_valid_cutoff to be {} rather than {}", $expected_valid_index, actual);
1238        let parts = RuneParts::from_raw_parts($ptr, $length);
1239        let result = parts.rune_at_index(actual);
1240        assert!(result.is_ok(), "expected valid Rune at index to be {} but got error: {}", $expected_valid_index, result.err().map(|err|err.to_string()).unwrap_or_default());
1241        let rune = result.unwrap();
1242        assert_eq!(rune.as_str(), $expected_rune_str,
1243                   "expected rune at index {} to match \"{}\" rather than \"{}\"",
1244                   actual, $expected_rune_str, rune.as_str());
1245    }};
1246}
1247
1248    #[macro_export]
1249    macro_rules! assert_none_previous_valid_cutoff {
1250    (
1251        $ptr:expr, $length:expr, $expected_length:literal, $invalid_index:literal $(,)?
1252    ) => {{
1253
1254
1255        // debug_et_diagnostics::step!(fg = (line!() as u8),format!("expecting previous_valid_cutoff from invalid index {} to be None",$invalid_index));
1256
1257        assert_eq!(
1258            $length, $expected_length,
1259            "expected length to be {} rather than {}",
1260            $expected_length, $length
1261        );
1262        let result = previous_valid_cutoff($ptr, $length, $invalid_index);
1263        assert!(
1264            result.is_none(),
1265            "expected previous_valid_cutoff at {} to not be None but is actually {:#?}",
1266            $invalid_index,
1267            result
1268        );
1269    }};
1270}
1271}