Skip to main content

ferroui_base/media/text_formatting/unicode/
bidi_algorithm.rs

1// Copyright (c) Six Labors.
2// Licensed under the Apache License, Version 2.0.
3// Ported from: https://github.com/SixLabors/Fonts/
4
5use std::collections::HashMap;
6
7use super::bidi_class::BidiClass;
8use super::bidi_data::{clear_then_reset_if_too_large, BidiData};
9use super::bidi_paired_bracket_type::BidiPairedBracketType;
10
11/// Maximum pairing depth for paired brackets
12const MAX_PAIRED_BRACKET_DEPTH: usize = 63;
13
14/// Implementation of Unicode bidirectional algorithm (UAX #9)
15/// <https://unicode.org/reports/tr9/>
16///
17/// The Bidi algorithm uses a number of memory arrays for resolved
18/// types, level information, bracket types, x9 removal maps and
19/// more...
20///
21/// This implementation of the BiDi algorithm has been designed
22/// to reduce memory pressure by re-using the same
23/// work buffers, so instances of this class should be re-used
24/// as much as possible.
25///
26/// The caller's tables (original classes, paired bracket types and values,
27/// and the optional output levels) are borrowed for the duration of a
28/// `process` call instead of being stored as slices like upstream does.
29#[derive(Debug)]
30pub struct BidiAlgorithm {
31    /// Whether the state is clean and can be reused without a reset.
32    has_clean_state: bool,
33
34    /// Try if the incoming data is known to contain brackets
35    has_brackets: bool,
36
37    /// True if the incoming data is known to contain embedding runs
38    has_embeddings: bool,
39
40    /// True if the incoming data is known to contain isolating runs
41    has_isolates: bool,
42
43    /// Mapping of isolate start/end pairs
44    ///
45    /// Maps the start index to the end index. (Upstream keeps a two directional
46    /// dictionary but only ever reads the forward direction.)
47    isolate_pairs: HashMap<usize, usize>,
48
49    /// The working BiDi classes
50    working_classes: Vec<BidiClass>,
51
52    /// The resolved levels (empty when the caller supplied the output buffer)
53    resolved_levels: Vec<i8>,
54
55    /// The resolve paragraph embedding level
56    paragraph_embedding_level: i8,
57
58    /// The status stack used during resolution of explicit
59    /// embedding and isolating runs
60    status_stack: Vec<Status>,
61
62    /// Mapping used to virtually remove characters for rule X9
63    x9_map: Vec<usize>,
64
65    /// Re-usable list of level runs
66    level_runs: Vec<LevelRun>,
67
68    /// Mapping for the current isolating sequence, built
69    /// by joining level runs from the x9 map.
70    isolated_run_mapping: Vec<usize>,
71
72    /// A stack of pending isolate openings used by find_isolate_pairs()
73    pending_isolate_openings: Vec<usize>,
74
75    /// Reusable list of pending opening brackets used by the
76    /// locate_paired_brackets method
77    pending_opening_brackets: Vec<usize>,
78
79    /// Resolved list of paired brackets
80    paired_brackets: Vec<BracketPair>,
81}
82
83impl Default for BidiAlgorithm {
84    fn default() -> Self {
85        Self::new()
86    }
87}
88
89impl BidiAlgorithm {
90    /// Initializes a new instance of the [`BidiAlgorithm`] class.
91    pub fn new() -> Self {
92        Self {
93            has_clean_state: true,
94            has_brackets: false,
95            has_embeddings: false,
96            has_isolates: false,
97            isolate_pairs: HashMap::new(),
98            working_classes: Vec::new(),
99            resolved_levels: Vec::new(),
100            paragraph_embedding_level: 0,
101            status_stack: Vec::new(),
102            x9_map: Vec::new(),
103            level_runs: Vec::new(),
104            isolated_run_mapping: Vec::new(),
105            pending_isolate_openings: Vec::new(),
106            pending_opening_brackets: Vec::new(),
107            paired_brackets: Vec::new(),
108        }
109    }
110
111    /// Gets the resolved levels.
112    ///
113    /// Empty when the last `process_classes` call was given an output buffer
114    /// (the levels were written there).
115    #[inline]
116    pub fn resolved_levels(&self) -> &[i8] {
117        &self.resolved_levels
118    }
119
120    /// Gets the resolved paragraph embedding level
121    #[inline]
122    pub fn resolved_paragraph_embedding_level(&self) -> i32 {
123        self.paragraph_embedding_level as i32
124    }
125
126    /// Process data from a BiDiData instance
127    ///
128    /// * `data` - The BiDi Unicode data.
129    ///
130    /// The classes of `data` are updated in place by rule N0 (nonspacing marks
131    /// after resolved brackets take the bracket direction), as upstream.
132    pub fn process(&mut self, data: &mut BidiData) {
133        let paragraph_embedding_level = data.paragraph_embedding_level();
134        let has_brackets = data.has_brackets();
135        let has_embeddings = data.has_embeddings();
136        let has_isolates = data.has_isolates();
137        let (classes, paired_bracket_types, paired_bracket_values) = data.parts_mut();
138
139        self.process_classes(
140            classes,
141            paired_bracket_types,
142            paired_bracket_values,
143            paragraph_embedding_level,
144            has_brackets,
145            has_embeddings,
146            has_isolates,
147            None,
148        );
149    }
150
151    /// Processes Bidi Data
152    ///
153    /// Panics when `out_levels` is given and its length differs from the input data.
154    #[allow(clippy::too_many_arguments)]
155    pub fn process_classes(
156        &mut self,
157        types: &mut [BidiClass],
158        paired_bracket_types: &[BidiPairedBracketType],
159        paired_bracket_values: &[i32],
160        paragraph_embedding_level: i8,
161        has_brackets: Option<bool>,
162        has_embeddings: Option<bool>,
163        has_isolates: Option<bool>,
164        out_levels: Option<&mut [i8]>,
165    ) {
166        // Reset state
167        self.reset();
168
169        if types.is_empty() {
170            return;
171        }
172
173        self.has_clean_state = false;
174
175        // Setup original types and working types
176        self.working_classes.extend_from_slice(types);
177
178        // Store things we know
179        self.has_brackets = has_brackets.unwrap_or(paired_bracket_types.len() == types.len());
180        self.has_embeddings = has_embeddings.unwrap_or(true);
181        self.has_isolates = has_isolates.unwrap_or(true);
182
183        // Find all isolate pairs
184        self.find_isolate_pairs(types);
185
186        // Resolve the paragraph embedding level
187        if paragraph_embedding_level == 2 {
188            self.paragraph_embedding_level = self.resolve_embedding_level_at(types, 0);
189        } else {
190            self.paragraph_embedding_level = paragraph_embedding_level;
191        }
192
193        // Create resolved levels buffer
194        match out_levels {
195            Some(out_levels) => {
196                if out_levels.len() != types.len() {
197                    panic!("Out levels must be the same length as the input data");
198                }
199
200                self.resolve(types, paired_bracket_types, paired_bracket_values, out_levels);
201            }
202            None => {
203                // The buffer is moved out while the algorithm runs so that it can be
204                // borrowed next to the other work buffers; its capacity is kept.
205                let mut resolved_levels = std::mem::take(&mut self.resolved_levels);
206                resolved_levels.clear();
207                resolved_levels.resize(types.len(), self.paragraph_embedding_level);
208
209                self.resolve(types, paired_bracket_types, paired_bracket_values, &mut resolved_levels);
210
211                self.resolved_levels = resolved_levels;
212            }
213        }
214    }
215
216    /// The steps of `process_classes` that follow the buffer set up.
217    fn resolve(
218        &mut self,
219        original_classes: &mut [BidiClass],
220        paired_bracket_types: &[BidiPairedBracketType],
221        paired_bracket_values: &[i32],
222        resolved_levels: &mut [i8],
223    ) {
224        // Resolve explicit embedding levels (Rules X1-X8)
225        self.resolve_explicit_embedding_levels(original_classes, resolved_levels);
226
227        // Build the rule X9 map
228        self.build_x9_removal_map(original_classes);
229
230        // Process all isolated run sequences
231        self.process_isolated_run_sequences(original_classes, paired_bracket_types, paired_bracket_values, resolved_levels);
232
233        // Reset whitespace levels
234        self.reset_whitespace_levels(original_classes, resolved_levels);
235
236        // Clean up
237        self.assign_levels_to_code_points_removed_by_x9(original_classes, resolved_levels);
238    }
239
240    /// Resolve the paragraph embedding level if not explicitly passed
241    /// by the caller. Also used by rule X5c for FSI isolating sequences.
242    ///
243    /// * `data` - The data to be evaluated; it must be the data of the last
244    ///   `process` call (or a prefix of it), because the isolate pairs found by
245    ///   that call are used to skip isolates.
246    ///
247    /// Returns the resolved embedding level
248    pub fn resolve_embedding_level(&self, data: &[BidiClass]) -> i8 {
249        self.resolve_embedding_level_at(data, 0)
250    }
251
252    /// `data_start` is the offset of `data` inside the processed classes
253    /// (the `Start` of the slice upstream).
254    fn resolve_embedding_level_at(&self, data: &[BidiClass], data_start: usize) -> i8 {
255        // P2
256        let mut i = 0;
257        while i < data.len() {
258            match data[i] {
259                BidiClass::LeftToRight => {
260                    // P3
261                    return 0;
262                }
263
264                BidiClass::ArabicLetter | BidiClass::RightToLeft => {
265                    // P3
266                    return 1;
267                }
268
269                BidiClass::FirstStrongIsolate | BidiClass::LeftToRightIsolate | BidiClass::RightToLeftIsolate => {
270                    // Skip isolate pairs
271                    // (Because we're working with a slice, we need to adjust the indices
272                    //  we're using for the isolatePairs map)
273                    match self.isolate_pairs.get(&(data_start + i)) {
274                        // An end before the slice cannot be expressed; the scan would
275                        // restart from a negative index upstream.
276                        Some(&end) => i = end.saturating_sub(data_start),
277                        None => i = data.len(),
278                    }
279                }
280
281                _ => {}
282            }
283
284            i += 1;
285        }
286
287        // P3
288        0
289    }
290
291    /// Build a list of matching isolates for a directionality slice
292    /// Implements BD9
293    fn find_isolate_pairs(&mut self, original_classes: &[BidiClass]) {
294        // Redundant?
295        if !self.has_isolates {
296            return;
297        }
298
299        // Lets double check this as we go and clear the flag
300        // if there actually aren't any isolate pairs as this might
301        // mean we can skip some later steps
302        self.has_isolates = false;
303
304        // BD9...
305        self.pending_isolate_openings.clear();
306
307        for (i, &t) in original_classes.iter().enumerate() {
308            match t {
309                BidiClass::LeftToRightIsolate | BidiClass::RightToLeftIsolate | BidiClass::FirstStrongIsolate => {
310                    self.pending_isolate_openings.push(i);
311                    self.has_isolates = true;
312                }
313                BidiClass::PopDirectionalIsolate => {
314                    if let Some(opening) = self.pending_isolate_openings.pop() {
315                        self.isolate_pairs.insert(opening, i);
316                    }
317
318                    self.has_isolates = true;
319                }
320                _ => {}
321            }
322        }
323    }
324
325    /// Resolve the explicit embedding levels from the original
326    /// data.  Implements rules X1 to X8.
327    fn resolve_explicit_embedding_levels(&mut self, original_classes: &[BidiClass], resolved_levels: &mut [i8]) {
328        // Redundant?
329        if !self.has_isolates && !self.has_embeddings {
330            return;
331        }
332
333        // Work variables
334        self.status_stack.clear();
335        let mut overflow_isolate_count = 0;
336        let mut overflow_embedding_count = 0;
337        let mut valid_isolate_count = 0;
338
339        // Constants
340        const MAX_STACK_DEPTH: i8 = 125;
341
342        // Rule X1 - setup initial state
343        self.status_stack.clear();
344
345        // Neutral
346        self.status_stack.push(Status::new(self.paragraph_embedding_level, BidiClass::OtherNeutral, false));
347
348        #[inline(always)]
349        fn peek(stack: &[Status]) -> Status {
350            *stack.last().expect("the status stack is never empty")
351        }
352
353        #[inline(always)]
354        fn next_odd_level(level: i8) -> i8 {
355            ((level as i32 + 1) | 1) as i8
356        }
357
358        #[inline(always)]
359        fn next_even_level(level: i8) -> i8 {
360            ((level as i32 + 2) & !1) as i8
361        }
362
363        // Process all characters
364        for i in 0..original_classes.len() {
365            match original_classes[i] {
366                BidiClass::RightToLeftEmbedding => {
367                    // Rule X2
368                    let new_level = next_odd_level(peek(&self.status_stack).embedding_level);
369                    if new_level <= MAX_STACK_DEPTH && overflow_isolate_count == 0 && overflow_embedding_count == 0 {
370                        self.status_stack.push(Status::new(new_level, BidiClass::OtherNeutral, false));
371                        resolved_levels[i] = new_level;
372                    } else if overflow_isolate_count == 0 {
373                        overflow_embedding_count += 1;
374                    }
375                }
376
377                BidiClass::LeftToRightEmbedding => {
378                    // Rule X3
379                    let new_level = next_even_level(peek(&self.status_stack).embedding_level);
380                    if new_level < MAX_STACK_DEPTH && overflow_isolate_count == 0 && overflow_embedding_count == 0 {
381                        self.status_stack.push(Status::new(new_level, BidiClass::OtherNeutral, false));
382                        resolved_levels[i] = new_level;
383                    } else if overflow_isolate_count == 0 {
384                        overflow_embedding_count += 1;
385                    }
386                }
387
388                BidiClass::RightToLeftOverride => {
389                    // Rule X4
390                    let new_level = next_odd_level(peek(&self.status_stack).embedding_level);
391                    if new_level <= MAX_STACK_DEPTH && overflow_isolate_count == 0 && overflow_embedding_count == 0 {
392                        self.status_stack.push(Status::new(new_level, BidiClass::RightToLeft, false));
393                        resolved_levels[i] = new_level;
394                    } else if overflow_isolate_count == 0 {
395                        overflow_embedding_count += 1;
396                    }
397                }
398
399                BidiClass::LeftToRightOverride => {
400                    // Rule X5
401                    let new_level = next_even_level(peek(&self.status_stack).embedding_level);
402                    if new_level <= MAX_STACK_DEPTH && overflow_isolate_count == 0 && overflow_embedding_count == 0 {
403                        self.status_stack.push(Status::new(new_level, BidiClass::LeftToRight, false));
404                        resolved_levels[i] = new_level;
405                    } else if overflow_isolate_count == 0 {
406                        overflow_embedding_count += 1;
407                    }
408                }
409
410                BidiClass::RightToLeftIsolate | BidiClass::LeftToRightIsolate | BidiClass::FirstStrongIsolate => {
411                    // Rule X5a, X5b and X5c
412                    let mut resolved_isolate = original_classes[i];
413
414                    if resolved_isolate == BidiClass::FirstStrongIsolate {
415                        let end_of_isolate = match self.isolate_pairs.get(&i) {
416                            Some(&end_of_isolate) => end_of_isolate,
417                            None => original_classes.len(),
418                        };
419
420                        // Rule X5c
421                        if self.resolve_embedding_level_at(&original_classes[i + 1..end_of_isolate], i + 1) == 1 {
422                            resolved_isolate = BidiClass::RightToLeftIsolate;
423                        } else {
424                            resolved_isolate = BidiClass::LeftToRightIsolate;
425                        }
426                    }
427
428                    // Replace RLI's level with current embedding level
429                    let tos = peek(&self.status_stack);
430                    resolved_levels[i] = tos.embedding_level;
431
432                    // Apply override
433                    if tos.override_status != BidiClass::OtherNeutral {
434                        self.working_classes[i] = tos.override_status;
435                    }
436
437                    // Work out new level
438                    let new_level = if resolved_isolate == BidiClass::RightToLeftIsolate {
439                        next_odd_level(tos.embedding_level)
440                    } else {
441                        next_even_level(tos.embedding_level)
442                    };
443
444                    // Valid?
445                    if new_level <= MAX_STACK_DEPTH && overflow_isolate_count == 0 && overflow_embedding_count == 0 {
446                        valid_isolate_count += 1;
447                        self.status_stack.push(Status::new(new_level, BidiClass::OtherNeutral, true));
448                    } else {
449                        overflow_isolate_count += 1;
450                    }
451                }
452
453                BidiClass::BoundaryNeutral => {
454                    // Mentioned in rule X6 - "for all types besides ..., BN, ..."
455                    // no-op
456                }
457
458                BidiClass::PopDirectionalIsolate => {
459                    // Rule X6a
460                    if overflow_isolate_count > 0 {
461                        overflow_isolate_count -= 1;
462                    } else if valid_isolate_count != 0 {
463                        overflow_embedding_count = 0;
464                        while !peek(&self.status_stack).isolate_status {
465                            self.status_stack.pop();
466                        }
467
468                        self.status_stack.pop();
469                        valid_isolate_count -= 1;
470                    }
471
472                    let tos = peek(&self.status_stack);
473                    resolved_levels[i] = tos.embedding_level;
474                    if tos.override_status != BidiClass::OtherNeutral {
475                        self.working_classes[i] = tos.override_status;
476                    }
477                }
478
479                BidiClass::PopDirectionalFormat => {
480                    // Rule X7
481                    if overflow_isolate_count == 0 {
482                        if overflow_embedding_count > 0 {
483                            overflow_embedding_count -= 1;
484                        } else if !peek(&self.status_stack).isolate_status && self.status_stack.len() >= 2 {
485                            self.status_stack.pop();
486                        }
487                    }
488                }
489
490                BidiClass::ParagraphSeparator => {
491                    // Rule X8
492                    resolved_levels[i] = self.paragraph_embedding_level;
493                }
494
495                _ => {
496                    // Rule X6
497                    let tos = peek(&self.status_stack);
498                    resolved_levels[i] = tos.embedding_level;
499                    if tos.override_status != BidiClass::OtherNeutral {
500                        self.working_classes[i] = tos.override_status;
501                    }
502                }
503            }
504        }
505    }
506
507    /// Build a map to the original data positions that excludes all
508    /// the types defined by rule X9
509    fn build_x9_removal_map(&mut self, original_classes: &[BidiClass]) {
510        // Reserve room for the x9 map
511        self.x9_map.clear();
512        self.x9_map.reserve(original_classes.len());
513
514        if self.has_embeddings || self.has_isolates {
515            // Build a map the removes all x9 characters
516            for (i, &original_class) in original_classes.iter().enumerate() {
517                if !is_removed_by_x9(original_class) {
518                    self.x9_map.push(i);
519                }
520            }
521        } else {
522            self.x9_map.extend(0..original_classes.len());
523        }
524    }
525
526    /// Find the original character index for an entry in the X9 map
527    ///
528    /// * `index` - Index in the x9 removal map
529    ///
530    /// Returns the index to the original data
531    #[inline(always)]
532    fn map_x9(&self, index: usize) -> usize {
533        self.x9_map[index]
534    }
535
536    /// Add a new level run
537    ///
538    /// This method resolves the sos and eos values for the run
539    /// and adds the run to the list
540    ///
541    /// * `start` - The index of the start of the run (in x9 removed units)
542    /// * `length` - The length of the run (in x9 removed units)
543    /// * `level` - The level of the run
544    fn add_level_run(
545        &mut self,
546        start: usize,
547        length: usize,
548        level: i32,
549        original_classes: &[BidiClass],
550        resolved_levels: &[i8],
551    ) {
552        // Get original indices to first and last character in this run
553        let first_char_index = self.map_x9(start);
554        let last_char_index = self.map_x9(start + length - 1);
555
556        // Work out sos
557        let prev_level = original_classes[..first_char_index]
558            .iter()
559            .rposition(|&original_class| !is_removed_by_x9(original_class))
560            .map_or(self.paragraph_embedding_level as i32, |i| resolved_levels[i] as i32);
561        let sos = direction_from_level(prev_level.max(level));
562
563        // Work out eos
564        let last_type = self.working_classes[last_char_index];
565        let next_level = if is_isolate_start(last_type) {
566            self.paragraph_embedding_level as i32
567        } else {
568            let mut i = last_char_index + 1;
569            while i < original_classes.len() && is_removed_by_x9(original_classes[i]) {
570                i += 1;
571            }
572
573            if i >= original_classes.len() {
574                self.paragraph_embedding_level as i32
575            } else {
576                resolved_levels[i] as i32
577            }
578        };
579
580        let eos = direction_from_level(next_level.max(level));
581
582        // Add the run
583        self.level_runs.push(LevelRun::new(start, length, level, sos, eos));
584    }
585
586    /// Find all runs of the same level, populating the level_runs
587    /// collection
588    fn find_level_runs(&mut self, original_classes: &[BidiClass], resolved_levels: &[i8]) {
589        let mut current_level: i32 = -1;
590        let mut run_start = 0;
591        for i in 0..self.x9_map.len() {
592            let level = resolved_levels[self.map_x9(i)] as i32;
593            if level == current_level {
594                continue;
595            }
596
597            if current_level != -1 {
598                self.add_level_run(run_start, i - run_start, current_level, original_classes, resolved_levels);
599            }
600
601            current_level = level;
602            run_start = i;
603        }
604
605        // Don't forget the final level run
606        if current_level != -1 {
607            self.add_level_run(
608                run_start,
609                self.x9_map.len() - run_start,
610                current_level,
611                original_classes,
612                resolved_levels,
613            );
614        }
615    }
616
617    /// Given a character index, find the level run that starts at that position
618    ///
619    /// * `index` - The index into the original (unmapped) data
620    ///
621    /// Returns the index of the run that starts at that index
622    fn find_run_for_index(&self, index: usize) -> usize {
623        for i in 0..self.level_runs.len() {
624            // Passed index is for the original non-x9 filtered data, however
625            // the level run ranges are for the x9 filtered data.  Convert before
626            // comparing
627            if self.map_x9(self.level_runs[i].start) == index {
628                return i;
629            }
630        }
631
632        panic!("Internal error");
633    }
634
635    /// Determine and the process all isolated run sequences
636    fn process_isolated_run_sequences(
637        &mut self,
638        original_classes: &mut [BidiClass],
639        paired_bracket_types: &[BidiPairedBracketType],
640        paired_bracket_values: &[i32],
641        resolved_levels: &mut [i8],
642    ) {
643        // Find all runs with the same level
644        self.find_level_runs(original_classes, resolved_levels);
645
646        // Process them one at a time by first building
647        // a mapping using slices from the x9 map for each
648        // run section that needs to be joined together to
649        // form an complete run.  That full run mapping
650        // will be placed in isolated_run_mapping and then
651        // processed by process_isolated_run_sequence().
652        while !self.level_runs.is_empty() {
653            // Clear the mapping
654            self.isolated_run_mapping.clear();
655
656            // Combine mappings from this run and all runs that continue on from it
657            let mut run_index = 0;
658            let mut eos;
659            let sos = self.level_runs[0].sos;
660            let level = self.level_runs[0].level;
661            loop {
662                // Get the run
663                // Remove this run as we've now processed it
664                let r = self.level_runs.remove(run_index);
665
666                // The eos of the isolating run is the eos of the
667                // last level run that comprises it.
668                eos = r.eos;
669
670                // Add the x9 map indices for the run range to the mapping
671                // for this isolated run
672                self.isolated_run_mapping.extend_from_slice(&self.x9_map[r.start..r.start + r.length]);
673
674                // Get the last character and see if it's an isolating run with a matching
675                // PDI and concatenate that run to this one
676                let last_character_index = self.isolated_run_mapping[self.isolated_run_mapping.len() - 1];
677                let last_type = original_classes[last_character_index];
678                let next_run_index =
679                    if is_isolate_start(last_type) { self.isolate_pairs.get(&last_character_index).copied() } else { None };
680
681                match next_run_index {
682                    Some(next_run_index) => {
683                        // Find the continuing run index
684                        run_index = self.find_run_for_index(next_run_index);
685                    }
686                    None => break,
687                }
688            }
689
690            // Process this isolated run
691            self.process_isolated_run_sequence(
692                sos,
693                eos,
694                level,
695                original_classes,
696                paired_bracket_types,
697                paired_bracket_values,
698                resolved_levels,
699            );
700        }
701    }
702
703    /// Process a single isolated run sequence, where the character sequence
704    /// mapping is currently held in isolated_run_mapping.
705    #[allow(clippy::too_many_arguments)]
706    fn process_isolated_run_sequence(
707        &mut self,
708        sos: BidiClass,
709        eos: BidiClass,
710        run_level: i32,
711        original_classes: &mut [BidiClass],
712        paired_bracket_types: &[BidiPairedBracketType],
713        paired_bracket_values: &[i32],
714        resolved_levels: &mut [i8],
715    ) {
716        let has_brackets = self.has_brackets;
717
718        // Create mappings onto the underlying data
719        let mut run = IsolatedRun {
720            map: &self.isolated_run_mapping,
721            resolved_classes: &mut self.working_classes,
722            original_classes,
723            levels: resolved_levels,
724            paired_bracket_types,
725            paired_bracket_values,
726            level: run_level,
727            direction: direction_from_level(run_level),
728            length: self.isolated_run_mapping.len(),
729        };
730        let run_length = run.length;
731
732        // Rule W1
733        // Also, set hasXX flags
734        let mut previous_class = sos;
735
736        const ISOLATE_MASK: u32 = (1u32 << BidiClass::LeftToRightIsolate as i32)
737            | (1u32 << BidiClass::RightToLeftIsolate as i32)
738            | (1u32 << BidiClass::FirstStrongIsolate as i32)
739            | (1u32 << BidiClass::PopDirectionalIsolate as i32);
740
741        const W_RULES_MASK: u32 = (1u32 << BidiClass::EuropeanNumber as i32)
742            | (1u32 << BidiClass::ArabicLetter as i32)
743            | (1u32 << BidiClass::EuropeanSeparator as i32)
744            | (1u32 << BidiClass::CommonSeparator as i32)
745            | (1u32 << BidiClass::ArabicNumber as i32)
746            | (1u32 << BidiClass::EuropeanTerminator as i32);
747
748        let mut w_rules: u32 = 0;
749
750        for i in 0..run_length {
751            let resolved_class = run.resolved_class(i);
752
753            if resolved_class == BidiClass::NonspacingMark {
754                run.set_resolved_class(i, previous_class);
755            } else {
756                let class_bit = 1u32 << resolved_class as i32;
757
758                if (class_bit & ISOLATE_MASK) != 0 {
759                    previous_class = BidiClass::OtherNeutral;
760                } else {
761                    w_rules |= class_bit & W_RULES_MASK;
762                    previous_class = resolved_class;
763                }
764            }
765        }
766
767        // By tracking the types of characters known to be in the current run, we can
768        // skip some of the rules that we know won't apply.
769        let has_en = (w_rules & (1u32 << BidiClass::EuropeanNumber as i32)) != 0;
770        let has_al = (w_rules & (1u32 << BidiClass::ArabicLetter as i32)) != 0;
771        let has_es = (w_rules & (1u32 << BidiClass::EuropeanSeparator as i32)) != 0;
772        let has_cs = (w_rules & (1u32 << BidiClass::CommonSeparator as i32)) != 0;
773        let mut has_an = (w_rules & (1u32 << BidiClass::ArabicNumber as i32)) != 0;
774        let has_et = (w_rules & (1u32 << BidiClass::EuropeanTerminator as i32)) != 0;
775
776        // Rule W2
777        if has_en {
778            for i in 0..run_length {
779                if run.resolved_class(i) != BidiClass::EuropeanNumber {
780                    continue;
781                }
782
783                for j in (0..i).rev() {
784                    let resolved_class = run.resolved_class(j);
785
786                    match resolved_class {
787                        BidiClass::LeftToRight | BidiClass::RightToLeft | BidiClass::ArabicLetter => {
788                            if resolved_class == BidiClass::ArabicLetter {
789                                run.set_resolved_class(i, BidiClass::ArabicNumber);
790                                has_an = true;
791                            }
792
793                            break;
794                        }
795                        _ => {}
796                    }
797                }
798            }
799        }
800
801        // Rule W3
802        if has_al {
803            for i in 0..run_length {
804                if run.resolved_class(i) == BidiClass::ArabicLetter {
805                    run.set_resolved_class(i, BidiClass::RightToLeft);
806                }
807            }
808        }
809
810        // Rule W4
811        if (has_es || has_cs) && (has_en || has_an) {
812            let mut i = 1;
813            while i + 1 < run_length {
814                let resolved_class = run.resolved_class(i);
815
816                if resolved_class == BidiClass::EuropeanSeparator {
817                    let previous_separator_class = run.resolved_class(i - 1);
818                    let next_separator_class = run.resolved_class(i + 1);
819
820                    if previous_separator_class == BidiClass::EuropeanNumber
821                        && next_separator_class == BidiClass::EuropeanNumber
822                    {
823                        // ES between EN and EN
824                        run.set_resolved_class(i, BidiClass::EuropeanNumber);
825                    }
826                } else if resolved_class == BidiClass::CommonSeparator {
827                    let previous_separator_class = run.resolved_class(i - 1);
828                    let next_separator_class = run.resolved_class(i + 1);
829
830                    if (previous_separator_class == BidiClass::ArabicNumber
831                        && next_separator_class == BidiClass::ArabicNumber)
832                        || (previous_separator_class == BidiClass::EuropeanNumber
833                            && next_separator_class == BidiClass::EuropeanNumber)
834                    {
835                        // CS between (AN and AN) or (EN and EN)
836                        run.set_resolved_class(i, previous_separator_class);
837                    }
838                }
839
840                i += 1;
841            }
842        }
843
844        // Rule W5
845        if has_et && has_en {
846            let mut i = 0;
847            while i < run_length {
848                if run.resolved_class(i) != BidiClass::EuropeanTerminator {
849                    i += 1;
850                    continue;
851                }
852
853                // Locate end of sequence
854                let sequence_start = i;
855                let mut sequence_end = i;
856
857                while sequence_end < run_length && run.resolved_class(sequence_end) == BidiClass::EuropeanTerminator {
858                    sequence_end += 1;
859                }
860
861                // Preceded by, or followed by EN?
862                if (if sequence_start == 0 { sos } else { run.resolved_class(sequence_start - 1) })
863                    == BidiClass::EuropeanNumber
864                    || (if sequence_end == run_length { eos } else { run.resolved_class(sequence_end) })
865                        == BidiClass::EuropeanNumber
866                {
867                    // Change the entire range
868                    while i < sequence_end {
869                        run.set_resolved_class(i, BidiClass::EuropeanNumber);
870                        i += 1;
871                    }
872                }
873
874                // continue at end of sequence
875                i = sequence_end;
876                i += 1;
877            }
878        }
879
880        // Rule W6
881        if has_es || has_et || has_cs {
882            for i in 0..run_length {
883                match run.resolved_class(i) {
884                    BidiClass::EuropeanSeparator | BidiClass::EuropeanTerminator | BidiClass::CommonSeparator => {
885                        run.set_resolved_class(i, BidiClass::OtherNeutral);
886                    }
887                    _ => {}
888                }
889            }
890        }
891
892        // Rule W7.
893        if has_en {
894            let mut previous_strong_class = sos;
895
896            for i in 0..run_length {
897                let resolved_class = run.resolved_class(i);
898
899                match resolved_class {
900                    BidiClass::EuropeanNumber => {
901                        // If prev strong type was an L change this to L too
902                        if previous_strong_class == BidiClass::LeftToRight {
903                            run.set_resolved_class(i, BidiClass::LeftToRight);
904                        }
905                    }
906
907                    BidiClass::LeftToRight | BidiClass::RightToLeft => {
908                        // Remember previous strong type (NB: AL should already be changed to R)
909                        previous_strong_class = resolved_class;
910                    }
911                    _ => {}
912                }
913            }
914        }
915
916        // Rule N0 - process bracket pairs
917        if has_brackets {
918            run.locate_paired_brackets(&mut self.pending_opening_brackets, &mut self.paired_brackets);
919
920            for i in 0..self.paired_brackets.len() {
921                let paired_bracket = self.paired_brackets[i];
922                let mut strong_direction = run.inspect_paired_bracket(paired_bracket);
923
924                // Case "d" - no strong types in the brackets, ignore
925                if strong_direction == BidiClass::OtherNeutral {
926                    continue;
927                }
928
929                // Case "b" - strong type found that matches the embedding direction
930                if (strong_direction == BidiClass::LeftToRight || strong_direction == BidiClass::RightToLeft)
931                    && strong_direction == run.direction
932                {
933                    run.set_paired_bracket_direction(paired_bracket, strong_direction);
934                    continue;
935                }
936
937                // Case "c" - found opposite strong type found, look before to establish context
938                strong_direction = run.inspect_before_paired_bracket(paired_bracket, sos);
939                if strong_direction == run.direction || strong_direction == BidiClass::OtherNeutral {
940                    strong_direction = run.direction;
941                }
942
943                run.set_paired_bracket_direction(paired_bracket, strong_direction);
944            }
945        }
946
947        // Rules N1 and N2 - resolve neutral types
948        let mut i = 0;
949        while i < run_length {
950            let resolved_class = run.resolved_class(i);
951
952            if is_neutral_class(resolved_class) {
953                // Locate end of sequence
954                let seq_start = i;
955                let mut seq_end = i;
956
957                while seq_end < run_length && is_neutral_class(run.resolved_class(seq_end)) {
958                    seq_end += 1;
959                }
960
961                // Work out the preceding class
962                let class_before = if seq_start == 0 {
963                    sos
964                } else {
965                    match run.resolved_class(seq_start - 1) {
966                        BidiClass::ArabicNumber | BidiClass::EuropeanNumber => BidiClass::RightToLeft,
967                        class_before => class_before,
968                    }
969                };
970
971                // Work out the following class
972                let class_after = if seq_end == run_length {
973                    eos
974                } else {
975                    match run.resolved_class(seq_end) {
976                        BidiClass::ArabicNumber | BidiClass::EuropeanNumber => BidiClass::RightToLeft,
977                        class_after => class_after,
978                    }
979                };
980
981                // Work out the final resolved type
982                let final_resolve_class = if class_before == class_after {
983                    // Rule N1
984                    class_before
985                } else {
986                    // Rule N2
987                    run.direction
988                };
989
990                // Apply changes
991                for j in seq_start..seq_end {
992                    run.set_resolved_class(j, final_resolve_class);
993                }
994
995                // continue after this run
996                i = seq_end;
997            }
998
999            i += 1;
1000        }
1001
1002        // Rules I1 and I2 - resolve implicit types
1003        if (run.level & 0x01) == 0 {
1004            // Rule I1 - even
1005            for i in 0..run_length {
1006                let resolved_class = run.resolved_class(i);
1007                let current_run_level = run.level_mut(i);
1008
1009                match resolved_class {
1010                    BidiClass::RightToLeft => {
1011                        *current_run_level = current_run_level.wrapping_add(1);
1012                    }
1013                    BidiClass::ArabicNumber | BidiClass::EuropeanNumber => {
1014                        *current_run_level = current_run_level.wrapping_add(2);
1015                    }
1016                    _ => {}
1017                }
1018            }
1019        } else {
1020            // Rule I2 - odd
1021            for i in 0..run_length {
1022                let resolved_class = run.resolved_class(i);
1023                let current_run_level = run.level_mut(i);
1024
1025                if resolved_class != BidiClass::RightToLeft {
1026                    *current_run_level = current_run_level.wrapping_add(1);
1027                }
1028            }
1029        }
1030    }
1031
1032    /// Resets whitespace levels. Implements rule L1
1033    fn reset_whitespace_levels(&self, original_classes: &[BidiClass], resolved_levels: &mut [i8]) {
1034        for i in 0..resolved_levels.len() {
1035            let original_class = original_classes[i];
1036
1037            match original_class {
1038                BidiClass::ParagraphSeparator | BidiClass::SegmentSeparator => {
1039                    // Rule L1, clauses one and two.
1040                    resolved_levels[i] = self.paragraph_embedding_level;
1041
1042                    // Rule L1, clause three.
1043                    for j in (0..i).rev() {
1044                        if is_whitespace(original_classes[j]) {
1045                            // including format codes
1046                            resolved_levels[j] = self.paragraph_embedding_level;
1047                        } else {
1048                            break;
1049                        }
1050                    }
1051                }
1052                _ => {}
1053            }
1054        }
1055
1056        // Rule L1, clause four.
1057        for j in (0..resolved_levels.len()).rev() {
1058            if is_whitespace(original_classes[j]) {
1059                // including format codes
1060                resolved_levels[j] = self.paragraph_embedding_level;
1061            } else {
1062                break;
1063            }
1064        }
1065    }
1066
1067    /// Assign levels to any characters that would be have been
1068    /// removed by rule X9.  The idea is to keep level runs together
1069    /// that would otherwise be broken by an interfering isolate/embedding
1070    /// control character.
1071    fn assign_levels_to_code_points_removed_by_x9(&mut self, original_classes: &[BidiClass], resolved_levels: &mut [i8]) {
1072        // Redundant?
1073        if !self.has_isolates && !self.has_embeddings {
1074            return;
1075        }
1076
1077        // No-op?
1078        if self.working_classes.is_empty() {
1079            return;
1080        }
1081
1082        // Fix up first character
1083        if resolved_levels[0] < 0 {
1084            resolved_levels[0] = self.paragraph_embedding_level;
1085        }
1086
1087        if is_removed_by_x9(original_classes[0]) {
1088            self.working_classes[0] = original_classes[0];
1089        }
1090
1091        for i in 1..self.working_classes.len() {
1092            let original_class = original_classes[i];
1093
1094            if is_removed_by_x9(original_class) {
1095                self.working_classes[i] = original_class;
1096                resolved_levels[i] = resolved_levels[i - 1];
1097            }
1098        }
1099    }
1100
1101    /// Resets the bidi algorithm to a clean state.
1102    pub fn reset(&mut self) {
1103        if self.has_clean_state {
1104            return;
1105        }
1106
1107        self.has_brackets = false;
1108        self.has_embeddings = false;
1109        self.has_isolates = false;
1110
1111        // 16 bytes per entry, like the key/value pairs upstream counts.
1112        if self.isolate_pairs.capacity().saturating_mul(std::mem::size_of::<(usize, usize)>()) > 1024 * 1024 {
1113            self.isolate_pairs = HashMap::new();
1114        } else {
1115            self.isolate_pairs.clear();
1116        }
1117
1118        clear_then_reset_if_too_large(&mut self.working_classes);
1119        clear_then_reset_if_too_large(&mut self.resolved_levels);
1120        self.paragraph_embedding_level = 0;
1121        clear_then_reset_if_too_large(&mut self.status_stack);
1122        clear_then_reset_if_too_large(&mut self.x9_map);
1123        clear_then_reset_if_too_large(&mut self.level_runs);
1124        clear_then_reset_if_too_large(&mut self.isolated_run_mapping);
1125        clear_then_reset_if_too_large(&mut self.pending_isolate_openings);
1126        clear_then_reset_if_too_large(&mut self.pending_opening_brackets);
1127        clear_then_reset_if_too_large(&mut self.paired_brackets);
1128
1129        self.has_clean_state = true;
1130    }
1131}
1132
1133/// The isolating run sequence currently being processed: views of the
1134/// per-codepoint tables through the run's index mapping (the mapped array
1135/// slices and the run level/direction/length fields upstream).
1136struct IsolatedRun<'a> {
1137    /// Mapping for the isolating sequence onto the underlying data
1138    map: &'a [usize],
1139
1140    /// The resolved (working) types
1141    resolved_classes: &'a mut [BidiClass],
1142
1143    /// The original types
1144    original_classes: &'a mut [BidiClass],
1145
1146    /// The resolved levels
1147    levels: &'a mut [i8],
1148
1149    /// The paired bracket types
1150    paired_bracket_types: &'a [BidiPairedBracketType],
1151
1152    /// The paired bracket values
1153    paired_bracket_values: &'a [i32],
1154
1155    /// The level of the isolating run
1156    level: i32,
1157
1158    /// The direction of the isolating run
1159    direction: BidiClass,
1160
1161    /// The length of the isolating run
1162    length: usize,
1163}
1164
1165impl IsolatedRun<'_> {
1166    #[inline(always)]
1167    fn resolved_class(&self, index: usize) -> BidiClass {
1168        self.resolved_classes[self.map[index]]
1169    }
1170
1171    #[inline(always)]
1172    fn set_resolved_class(&mut self, index: usize, value: BidiClass) {
1173        self.resolved_classes[self.map[index]] = value;
1174    }
1175
1176    #[inline(always)]
1177    fn original_class(&self, index: usize) -> BidiClass {
1178        self.original_classes[self.map[index]]
1179    }
1180
1181    #[inline(always)]
1182    fn set_original_class(&mut self, index: usize, value: BidiClass) {
1183        self.original_classes[self.map[index]] = value;
1184    }
1185
1186    #[inline(always)]
1187    fn level_mut(&mut self, index: usize) -> &mut i8 {
1188        &mut self.levels[self.map[index]]
1189    }
1190
1191    #[inline(always)]
1192    fn paired_bracket_type(&self, index: usize) -> BidiPairedBracketType {
1193        self.paired_bracket_types[self.map[index]]
1194    }
1195
1196    #[inline(always)]
1197    fn paired_bracket_value(&self, index: usize) -> i32 {
1198        self.paired_bracket_values[self.map[index]]
1199    }
1200
1201    /// Locate all pair brackets in the current isolating run
1202    ///
1203    /// Leaves a sorted list of BracketPairs in `paired_brackets`
1204    fn locate_paired_brackets(&self, pending_opening_brackets: &mut Vec<usize>, paired_brackets: &mut Vec<BracketPair>) {
1205        // Clear work collections
1206        pending_opening_brackets.clear();
1207        paired_brackets.clear();
1208
1209        // Since sorting is expensive if called often
1210        // and since we will rarely have many
1211        // items in this list (most paragraphs will only have a handful of bracket
1212        // pairs - if that), we use a simple linear lookup and insert most of the
1213        // time.  If there are more that `SORT_LIMIT` paired brackets we abort th
1214        // linear searching/inserting and sort at the end.
1215        const SORT_LIMIT: usize = 8;
1216
1217        // Process all characters in the run, looking for paired brackets
1218        for i in 0..self.length {
1219            // Ignore non-neutral characters
1220            if self.resolved_class(i) != BidiClass::OtherNeutral {
1221                continue;
1222            }
1223
1224            match self.paired_bracket_type(i) {
1225                BidiPairedBracketType::Open => {
1226                    if pending_opening_brackets.len() == MAX_PAIRED_BRACKET_DEPTH {
1227                        break;
1228                    }
1229
1230                    pending_opening_brackets.insert(0, i);
1231                }
1232
1233                BidiPairedBracketType::Close => {
1234                    // see if there is a match
1235                    for j in 0..pending_opening_brackets.len() {
1236                        if self.paired_bracket_value(i) != self.paired_bracket_value(pending_opening_brackets[j]) {
1237                            continue;
1238                        }
1239
1240                        // Add this paired bracket set
1241                        let opener = pending_opening_brackets[j];
1242                        if paired_brackets.len() < SORT_LIMIT {
1243                            let mut ppi = 0;
1244                            while ppi < paired_brackets.len() && paired_brackets[ppi].opening_index < opener {
1245                                ppi += 1;
1246                            }
1247
1248                            paired_brackets.insert(ppi, BracketPair::new(opener, i));
1249                        } else {
1250                            paired_brackets.push(BracketPair::new(opener, i));
1251                        }
1252
1253                        // remove up to and including matched opener
1254                        pending_opening_brackets.drain(0..=j);
1255                        break;
1256                    }
1257                }
1258
1259                BidiPairedBracketType::None => {}
1260            }
1261        }
1262
1263        // Is a sort pending?
1264        if paired_brackets.len() > SORT_LIMIT {
1265            paired_brackets.sort_unstable_by_key(|pair| pair.opening_index);
1266        }
1267    }
1268
1269    /// Inspect a paired bracket set and determine its strong direction
1270    ///
1271    /// * `bracket_pair` - The paired bracket to be inspected
1272    ///
1273    /// Returns the direction of the bracket set content
1274    fn inspect_paired_bracket(&self, bracket_pair: BracketPair) -> BidiClass {
1275        let direction_from_level = direction_from_level(self.level);
1276        let mut direction_opposite = BidiClass::OtherNeutral;
1277        for i in bracket_pair.opening_index + 1..bracket_pair.closing_index {
1278            let dir = get_strong_class_n0(self.resolved_class(i));
1279            if dir == BidiClass::OtherNeutral {
1280                continue;
1281            }
1282
1283            if dir == direction_from_level {
1284                return dir;
1285            }
1286
1287            direction_opposite = dir;
1288        }
1289
1290        direction_opposite
1291    }
1292
1293    /// Look for a strong type before a paired bracket
1294    ///
1295    /// * `bracket_pair` - The paired bracket set to be inspected
1296    /// * `sos` - The sos in case nothing found before the bracket
1297    ///
1298    /// Returns the strong direction before the brackets
1299    fn inspect_before_paired_bracket(&self, bracket_pair: BracketPair, sos: BidiClass) -> BidiClass {
1300        for i in (0..bracket_pair.opening_index).rev() {
1301            let direction = get_strong_class_n0(self.resolved_class(i));
1302            if direction != BidiClass::OtherNeutral {
1303                return direction;
1304            }
1305        }
1306
1307        sos
1308    }
1309
1310    /// Sets the direction of a bracket pair, including setting the direction of
1311    /// NSM's inside the brackets and following.
1312    ///
1313    /// * `paired_bracket` - The paired brackets
1314    /// * `direction` - The resolved direction for the bracket pair
1315    fn set_paired_bracket_direction(&mut self, paired_bracket: BracketPair, direction: BidiClass) {
1316        // Set the direction of the brackets
1317        self.set_resolved_class(paired_bracket.opening_index, direction);
1318        self.set_resolved_class(paired_bracket.closing_index, direction);
1319
1320        // Set the directionality of NSM's inside the brackets
1321        // BN  characters (such as ZWJ or ZWSP) that appear between the base bracket character
1322        // and the nonspacing mark should be ignored.
1323        for i in paired_bracket.opening_index + 1..paired_bracket.closing_index {
1324            if self.original_class(i) == BidiClass::NonspacingMark {
1325                self.set_original_class(i, direction);
1326            } else if self.original_class(i) != BidiClass::BoundaryNeutral {
1327                break;
1328            }
1329        }
1330
1331        // Set the directionality of NSM's following the brackets
1332        for i in paired_bracket.closing_index + 1..self.length {
1333            if self.original_class(i) == BidiClass::NonspacingMark {
1334                self.set_original_class(i, direction);
1335            } else if self.original_class(i) != BidiClass::BoundaryNeutral {
1336                break;
1337            }
1338        }
1339    }
1340}
1341
1342#[inline(always)]
1343fn is_isolate_start(class: BidiClass) -> bool {
1344    const MASK: u32 = (1u32 << BidiClass::LeftToRightIsolate as i32)
1345        | (1u32 << BidiClass::RightToLeftIsolate as i32)
1346        | (1u32 << BidiClass::FirstStrongIsolate as i32);
1347    ((1u32 << class as i32) & MASK) != 0
1348}
1349
1350/// Check if a directionality type represents whitespace
1351#[inline(always)]
1352fn is_whitespace(bi_di_class: BidiClass) -> bool {
1353    const MASK: u32 = (1u32 << BidiClass::LeftToRightEmbedding as i32)
1354        | (1u32 << BidiClass::RightToLeftEmbedding as i32)
1355        | (1u32 << BidiClass::LeftToRightOverride as i32)
1356        | (1u32 << BidiClass::RightToLeftOverride as i32)
1357        | (1u32 << BidiClass::PopDirectionalFormat as i32)
1358        | (1u32 << BidiClass::LeftToRightIsolate as i32)
1359        | (1u32 << BidiClass::RightToLeftIsolate as i32)
1360        | (1u32 << BidiClass::FirstStrongIsolate as i32)
1361        | (1u32 << BidiClass::PopDirectionalIsolate as i32)
1362        | (1u32 << BidiClass::BoundaryNeutral as i32)
1363        | (1u32 << BidiClass::WhiteSpace as i32);
1364    ((1u32 << bi_di_class as i32) & MASK) != 0
1365}
1366
1367/// Convert a level to a direction where odd is RTL and
1368/// even is LTR
1369///
1370/// * `level` - The level to convert
1371///
1372/// Returns a directionality
1373#[inline(always)]
1374fn direction_from_level(level: i32) -> BidiClass {
1375    if (level & 0x1) == 0 {
1376        BidiClass::LeftToRight
1377    } else {
1378        BidiClass::RightToLeft
1379    }
1380}
1381
1382/// Helper to check if a directionality is removed by rule X9
1383///
1384/// * `bi_di_class` - The bidi type to check
1385///
1386/// Returns true if rule X9 would remove this character; otherwise false
1387#[inline(always)]
1388fn is_removed_by_x9(bi_di_class: BidiClass) -> bool {
1389    const MASK: u32 = (1u32 << BidiClass::LeftToRightEmbedding as i32)
1390        | (1u32 << BidiClass::RightToLeftEmbedding as i32)
1391        | (1u32 << BidiClass::LeftToRightOverride as i32)
1392        | (1u32 << BidiClass::RightToLeftOverride as i32)
1393        | (1u32 << BidiClass::PopDirectionalFormat as i32)
1394        | (1u32 << BidiClass::BoundaryNeutral as i32);
1395    ((1u32 << bi_di_class as i32) & MASK) != 0
1396}
1397
1398/// Check if a directionality is neutral for rules N1 and N2
1399#[inline(always)]
1400fn is_neutral_class(direction: BidiClass) -> bool {
1401    const MASK: u32 = (1u32 << BidiClass::ParagraphSeparator as i32)
1402        | (1u32 << BidiClass::SegmentSeparator as i32)
1403        | (1u32 << BidiClass::WhiteSpace as i32)
1404        | (1u32 << BidiClass::OtherNeutral as i32)
1405        | (1u32 << BidiClass::RightToLeftIsolate as i32)
1406        | (1u32 << BidiClass::LeftToRightIsolate as i32)
1407        | (1u32 << BidiClass::FirstStrongIsolate as i32)
1408        | (1u32 << BidiClass::PopDirectionalIsolate as i32);
1409    ((1u32 << direction as i32) & MASK) != 0
1410}
1411
1412/// Maps a direction to a strong class for rule N0
1413///
1414/// * `direction` - The direction to map
1415///
1416/// Returns a strong direction - R, L or ON
1417#[inline(always)]
1418fn get_strong_class_n0(direction: BidiClass) -> BidiClass {
1419    match direction {
1420        BidiClass::EuropeanNumber | BidiClass::ArabicNumber | BidiClass::ArabicLetter | BidiClass::RightToLeft => {
1421            BidiClass::RightToLeft
1422        }
1423        BidiClass::LeftToRight => BidiClass::LeftToRight,
1424        _ => BidiClass::OtherNeutral,
1425    }
1426}
1427
1428/// Hold the start and end index of a pair of brackets
1429#[derive(Clone, Copy, Debug)]
1430struct BracketPair {
1431    /// The index of the opening bracket
1432    opening_index: usize,
1433
1434    /// The index of the closing bracket
1435    closing_index: usize,
1436}
1437
1438impl BracketPair {
1439    /// Initializes a new instance of the [`BracketPair`] struct.
1440    ///
1441    /// * `opening_index` - Index of the opening bracket
1442    /// * `closing_index` - Index of the closing bracket
1443    #[inline]
1444    fn new(opening_index: usize, closing_index: usize) -> Self {
1445        Self { opening_index, closing_index }
1446    }
1447}
1448
1449/// Status stack entry used while resolving explicit
1450/// embedding levels
1451#[derive(Clone, Copy, Debug)]
1452struct Status {
1453    embedding_level: i8,
1454    override_status: BidiClass,
1455    isolate_status: bool,
1456}
1457
1458impl Status {
1459    #[inline]
1460    fn new(embedding_level: i8, override_status: BidiClass, isolate_status: bool) -> Self {
1461        Self { embedding_level, override_status, isolate_status }
1462    }
1463}
1464
1465/// Provides information about a level run - a continuous
1466/// sequence of equal levels.
1467#[derive(Clone, Copy, Debug)]
1468struct LevelRun {
1469    start: usize,
1470    length: usize,
1471    level: i32,
1472    sos: BidiClass,
1473    eos: BidiClass,
1474}
1475
1476impl LevelRun {
1477    #[inline]
1478    fn new(start: usize, length: usize, level: i32, sos: BidiClass, eos: BidiClass) -> Self {
1479        Self { start, length, level, sos, eos }
1480    }
1481}