diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /third_party/rust/unicode-bidi/src/level.rs | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/unicode-bidi/src/level.rs')
-rw-r--r-- | third_party/rust/unicode-bidi/src/level.rs | 385 |
1 files changed, 385 insertions, 0 deletions
diff --git a/third_party/rust/unicode-bidi/src/level.rs b/third_party/rust/unicode-bidi/src/level.rs new file mode 100644 index 0000000000..f2e0d99ab7 --- /dev/null +++ b/third_party/rust/unicode-bidi/src/level.rs @@ -0,0 +1,385 @@ +// Copyright 2017 The Servo Project Developers. See the +// COPYRIGHT file at the top-level directory of this distribution. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Bidi Embedding Level +//! +//! See [`Level`](struct.Level.html) for more details. +//! +//! <http://www.unicode.org/reports/tr9/#BD2> + +use alloc::string::{String, ToString}; +use alloc::vec::Vec; +use core::convert::{From, Into}; + +use super::char_data::BidiClass; + +/// Embedding Level +/// +/// Embedding Levels are numbers between 0 and 126 (inclusive), where even values denote a +/// left-to-right (LTR) direction and odd values a right-to-left (RTL) direction. +/// +/// This struct maintains a *valid* status for level numbers, meaning that creating a new level, or +/// mutating an existing level, with the value smaller than `0` (before conversion to `u8`) or +/// larger than 125 results in an `Error`. +/// +/// <http://www.unicode.org/reports/tr9/#BD2> +#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct Level(u8); + +pub const LTR_LEVEL: Level = Level(0); +pub const RTL_LEVEL: Level = Level(1); + +const MAX_DEPTH: u8 = 125; +/// During explicit level resolution, embedding level can go as high as `max_depth`. +pub const MAX_EXPLICIT_DEPTH: u8 = MAX_DEPTH; +/// During implicit level resolution, embedding level can go as high as `max_depth + 1`. +pub const MAX_IMPLICIT_DEPTH: u8 = MAX_DEPTH + 1; + +/// Errors that can occur on Level creation or mutation +#[derive(Debug, PartialEq)] +pub enum Error { + /// Out-of-range (invalid) embedding level number. + OutOfRangeNumber, +} + +impl Level { + /// New LTR level with smallest number value (0). + #[inline] + pub fn ltr() -> Level { + LTR_LEVEL + } + + /// New RTL level with smallest number value (1). + #[inline] + pub fn rtl() -> Level { + RTL_LEVEL + } + + /// Maximum depth of the directional status stack during implicit resolutions. + pub fn max_implicit_depth() -> u8 { + MAX_IMPLICIT_DEPTH + } + + /// Maximum depth of the directional status stack during explicit resolutions. + pub fn max_explicit_depth() -> u8 { + MAX_EXPLICIT_DEPTH + } + + // == Inquiries == + + /// Create new level, fail if number is larger than `max_depth + 1`. + #[inline] + pub fn new(number: u8) -> Result<Level, Error> { + if number <= MAX_IMPLICIT_DEPTH { + Ok(Level(number)) + } else { + Err(Error::OutOfRangeNumber) + } + } + + /// Create new level, fail if number is larger than `max_depth`. + #[inline] + pub fn new_explicit(number: u8) -> Result<Level, Error> { + if number <= MAX_EXPLICIT_DEPTH { + Ok(Level(number)) + } else { + Err(Error::OutOfRangeNumber) + } + } + + // == Inquiries == + + /// The level number. + #[inline] + pub fn number(&self) -> u8 { + self.0 + } + + /// If this level is left-to-right. + #[inline] + pub fn is_ltr(&self) -> bool { + self.0 % 2 == 0 + } + + /// If this level is right-to-left. + #[inline] + pub fn is_rtl(&self) -> bool { + self.0 % 2 == 1 + } + + // == Mutators == + + /// Raise level by `amount`, fail if number is larger than `max_depth + 1`. + #[inline] + pub fn raise(&mut self, amount: u8) -> Result<(), Error> { + match self.0.checked_add(amount) { + Some(number) => { + if number <= MAX_IMPLICIT_DEPTH { + self.0 = number; + Ok(()) + } else { + Err(Error::OutOfRangeNumber) + } + } + None => Err(Error::OutOfRangeNumber), + } + } + + /// Raise level by `amount`, fail if number is larger than `max_depth`. + #[inline] + pub fn raise_explicit(&mut self, amount: u8) -> Result<(), Error> { + match self.0.checked_add(amount) { + Some(number) => { + if number <= MAX_EXPLICIT_DEPTH { + self.0 = number; + Ok(()) + } else { + Err(Error::OutOfRangeNumber) + } + } + None => Err(Error::OutOfRangeNumber), + } + } + + /// Lower level by `amount`, fail if number goes below zero. + #[inline] + pub fn lower(&mut self, amount: u8) -> Result<(), Error> { + match self.0.checked_sub(amount) { + Some(number) => { + self.0 = number; + Ok(()) + } + None => Err(Error::OutOfRangeNumber), + } + } + + // == Helpers == + + /// The next LTR (even) level greater than this, or fail if number is larger than `max_depth`. + #[inline] + pub fn new_explicit_next_ltr(&self) -> Result<Level, Error> { + Level::new_explicit((self.0 + 2) & !1) + } + + /// The next RTL (odd) level greater than this, or fail if number is larger than `max_depth`. + #[inline] + pub fn new_explicit_next_rtl(&self) -> Result<Level, Error> { + Level::new_explicit((self.0 + 1) | 1) + } + + /// The lowest RTL (odd) level greater than or equal to this, or fail if number is larger than + /// `max_depth + 1`. + #[inline] + pub fn new_lowest_ge_rtl(&self) -> Result<Level, Error> { + Level::new(self.0 | 1) + } + + /// Generate a character type based on a level (as specified in steps X10 and N2). + #[inline] + pub fn bidi_class(&self) -> BidiClass { + if self.is_rtl() { + BidiClass::R + } else { + BidiClass::L + } + } + + pub fn vec(v: &[u8]) -> Vec<Level> { + v.iter().map(|&x| x.into()).collect() + } +} + +/// If levels has any RTL (odd) level +/// +/// This information is usually used to skip re-ordering of text when no RTL level is present +#[inline] +pub fn has_rtl(levels: &[Level]) -> bool { + levels.iter().any(|&lvl| lvl.is_rtl()) +} + +impl Into<u8> for Level { + /// Convert to the level number + #[inline] + fn into(self) -> u8 { + self.number() + } +} + +impl From<u8> for Level { + /// Create level by number + #[inline] + fn from(number: u8) -> Level { + Level::new(number).expect("Level number error") + } +} + +/// Used for matching levels in conformance tests +impl<'a> PartialEq<&'a str> for Level { + #[inline] + fn eq(&self, s: &&'a str) -> bool { + *s == "x" || *s == self.0.to_string() + } +} + +/// Used for matching levels in conformance tests +impl<'a> PartialEq<String> for Level { + #[inline] + fn eq(&self, s: &String) -> bool { + self == &s.as_str() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_new() { + assert_eq!(Level::new(0), Ok(Level(0))); + assert_eq!(Level::new(1), Ok(Level(1))); + assert_eq!(Level::new(10), Ok(Level(10))); + assert_eq!(Level::new(125), Ok(Level(125))); + assert_eq!(Level::new(126), Ok(Level(126))); + assert_eq!(Level::new(127), Err(Error::OutOfRangeNumber)); + assert_eq!(Level::new(255), Err(Error::OutOfRangeNumber)); + } + + #[test] + fn test_new_explicit() { + assert_eq!(Level::new_explicit(0), Ok(Level(0))); + assert_eq!(Level::new_explicit(1), Ok(Level(1))); + assert_eq!(Level::new_explicit(10), Ok(Level(10))); + assert_eq!(Level::new_explicit(125), Ok(Level(125))); + assert_eq!(Level::new_explicit(126), Err(Error::OutOfRangeNumber)); + assert_eq!(Level::new_explicit(255), Err(Error::OutOfRangeNumber)); + } + + #[test] + fn test_is_ltr() { + assert_eq!(Level(0).is_ltr(), true); + assert_eq!(Level(1).is_ltr(), false); + assert_eq!(Level(10).is_ltr(), true); + assert_eq!(Level(11).is_ltr(), false); + assert_eq!(Level(124).is_ltr(), true); + assert_eq!(Level(125).is_ltr(), false); + } + + #[test] + fn test_is_rtl() { + assert_eq!(Level(0).is_rtl(), false); + assert_eq!(Level(1).is_rtl(), true); + assert_eq!(Level(10).is_rtl(), false); + assert_eq!(Level(11).is_rtl(), true); + assert_eq!(Level(124).is_rtl(), false); + assert_eq!(Level(125).is_rtl(), true); + } + + #[test] + fn test_raise() { + let mut level = Level::ltr(); + assert_eq!(level.number(), 0); + assert!(level.raise(100).is_ok()); + assert_eq!(level.number(), 100); + assert!(level.raise(26).is_ok()); + assert_eq!(level.number(), 126); + assert!(level.raise(1).is_err()); // invalid! + assert!(level.raise(250).is_err()); // overflow! + assert_eq!(level.number(), 126); + } + + #[test] + fn test_raise_explicit() { + let mut level = Level::ltr(); + assert_eq!(level.number(), 0); + assert!(level.raise_explicit(100).is_ok()); + assert_eq!(level.number(), 100); + assert!(level.raise_explicit(25).is_ok()); + assert_eq!(level.number(), 125); + assert!(level.raise_explicit(1).is_err()); // invalid! + assert!(level.raise_explicit(250).is_err()); // overflow! + assert_eq!(level.number(), 125); + } + + #[test] + fn test_lower() { + let mut level = Level::rtl(); + assert_eq!(level.number(), 1); + assert!(level.lower(1).is_ok()); + assert_eq!(level.number(), 0); + assert!(level.lower(1).is_err()); // underflow! + assert!(level.lower(250).is_err()); // underflow! + assert_eq!(level.number(), 0); + } + + #[test] + fn test_has_rtl() { + assert_eq!(has_rtl(&Level::vec(&[0, 0, 0])), false); + assert_eq!(has_rtl(&Level::vec(&[0, 1, 0])), true); + assert_eq!(has_rtl(&Level::vec(&[0, 2, 0])), false); + assert_eq!(has_rtl(&Level::vec(&[0, 125, 0])), true); + assert_eq!(has_rtl(&Level::vec(&[0, 126, 0])), false); + } + + #[test] + fn test_into() { + let level = Level::rtl(); + let number: u8 = level.into(); + assert_eq!(1u8, number); + } + + #[test] + fn test_vec() { + assert_eq!( + Level::vec(&[0, 1, 125]), + vec![Level(0), Level(1), Level(125)] + ); + } + + #[test] + fn test_str_eq() { + assert_eq!(Level::vec(&[0, 1, 4, 125]), vec!["0", "1", "x", "125"]); + assert_ne!(Level::vec(&[0, 1, 4, 125]), vec!["0", "1", "5", "125"]); + } + + #[test] + fn test_string_eq() { + assert_eq!( + Level::vec(&[0, 1, 4, 125]), + vec!["0".to_string(), "1".to_string(), "x".to_string(), "125".to_string()] + ); + } +} + +#[cfg(all(feature = "serde", test))] +mod serde_tests { + use super::*; + use serde_test::{assert_tokens, Token}; + + #[test] + fn test_statics() { + assert_tokens( + &Level::ltr(), + &[Token::NewtypeStruct { name: "Level" }, Token::U8(0)], + ); + assert_tokens( + &Level::rtl(), + &[Token::NewtypeStruct { name: "Level" }, Token::U8(1)], + ); + } + + #[test] + fn test_new() { + let level = Level::new(42).unwrap(); + assert_tokens( + &level, + &[Token::NewtypeStruct { name: "Level" }, Token::U8(42)], + ); + } +} |