Skip to main content

servo_base/
text.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use std::fmt;
6use std::iter::Sum;
7use std::ops::{Add, AddAssign, Range, Sub, SubAssign};
8
9use malloc_size_of_derive::MallocSizeOf;
10
11pub use crate::unicode_block::{UnicodeBlock, UnicodeBlockMethod};
12
13pub fn is_bidi_control(c: char) -> bool {
14    matches!(c, '\u{202A}'..='\u{202E}' | '\u{2066}'..='\u{2069}' | '\u{200E}' | '\u{200F}' | '\u{061C}')
15}
16
17pub fn unicode_plane(codepoint: char) -> u32 {
18    (codepoint as u32) >> 16
19}
20
21pub fn is_cjk(codepoint: char) -> bool {
22    if let Some(
23        UnicodeBlock::CJKRadicalsSupplement |
24        UnicodeBlock::KangxiRadicals |
25        UnicodeBlock::IdeographicDescriptionCharacters |
26        UnicodeBlock::CJKSymbolsandPunctuation |
27        UnicodeBlock::Hiragana |
28        UnicodeBlock::Katakana |
29        UnicodeBlock::Bopomofo |
30        UnicodeBlock::HangulCompatibilityJamo |
31        UnicodeBlock::Kanbun |
32        UnicodeBlock::BopomofoExtended |
33        UnicodeBlock::CJKStrokes |
34        UnicodeBlock::KatakanaPhoneticExtensions |
35        UnicodeBlock::EnclosedCJKLettersandMonths |
36        UnicodeBlock::CJKCompatibility |
37        UnicodeBlock::CJKUnifiedIdeographsExtensionA |
38        UnicodeBlock::YijingHexagramSymbols |
39        UnicodeBlock::CJKUnifiedIdeographs |
40        UnicodeBlock::CJKCompatibilityIdeographs |
41        UnicodeBlock::CJKCompatibilityForms |
42        UnicodeBlock::HalfwidthandFullwidthForms,
43    ) = codepoint.block()
44    {
45        return true;
46    }
47
48    // https://en.wikipedia.org/wiki/Plane_(Unicode)#Supplementary_Ideographic_Plane
49    // https://en.wikipedia.org/wiki/Plane_(Unicode)#Tertiary_Ideographic_Plane
50    unicode_plane(codepoint) == 2 || unicode_plane(codepoint) == 3
51}
52
53/// Equivalent to either `Range`, `RangeTo`, `RangeFrom`, or `RangeFull`
54#[derive(Clone, Copy, Eq, PartialEq, MallocSizeOf)]
55pub struct RangeAny<T>(RangeAnyInner<T>);
56
57#[derive(Clone, Copy, Eq, PartialEq, MallocSizeOf)]
58enum RangeAnyInner<T> {
59    Range { start: T, end: T },
60    RangeFrom { start: T },
61    RangeTo { end: T },
62    RangeFull,
63}
64
65size_of_test!(RangeAny<u32>, 12);
66size_of_test!(Option<RangeAny<u32>>, 12);
67
68impl<T: fmt::Debug> fmt::Debug for RangeAny<T> {
69    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
70        match &self.0 {
71            RangeAnyInner::Range { start, end } => write!(f, "{start:?}..{end:?}"),
72            RangeAnyInner::RangeFrom { start } => write!(f, "{start:?}.."),
73            RangeAnyInner::RangeTo { end, .. } => write!(f, "..{end:?}"),
74            RangeAnyInner::RangeFull => write!(f, ".."),
75        }
76    }
77}
78
79impl<T> RangeAny<T> {
80    pub fn new(start: Option<T>, end: Option<T>) -> Self {
81        Self(match (start, end) {
82            (Some(start), Some(end)) => RangeAnyInner::Range { start, end },
83            (Some(start), None) => RangeAnyInner::RangeFrom { start },
84            (None, Some(end)) => RangeAnyInner::RangeTo { end },
85            (None, None) => RangeAnyInner::RangeFull,
86        })
87    }
88
89    /// Returns a `RangeAny` that represents the range from the start to the given end.
90    pub fn from_start_to(end: T) -> Self {
91        Self(RangeAnyInner::RangeTo { end })
92    }
93
94    /// Returns a `RangeAny` that represents the full range: both bounds unset
95    pub fn full() -> Self {
96        Self(RangeAnyInner::RangeFull)
97    }
98
99    // Note: for a fully-generic general purpose container we’d return `Option<&T>`
100    // and remove the `Copy` bound, but Servo only uses `RangeAny` with `Utf*CodeUnits` types
101    // that implement `Copy`, so relying on `Copy` makes callers less verbose.
102    pub fn start(&self) -> Option<T>
103    where
104        T: Copy,
105    {
106        match self.0 {
107            RangeAnyInner::Range { start, .. } | RangeAnyInner::RangeFrom { start } => Some(start),
108            RangeAnyInner::RangeTo { .. } | RangeAnyInner::RangeFull => None,
109        }
110    }
111
112    pub fn end(&self) -> Option<T>
113    where
114        T: Copy,
115    {
116        match self.0 {
117            RangeAnyInner::Range { end, .. } | RangeAnyInner::RangeTo { end, .. } => Some(end),
118            RangeAnyInner::RangeFrom { .. } | RangeAnyInner::RangeFull => None,
119        }
120    }
121
122    /// Apply `Option::map` to each bound of this range
123    pub fn map<U>(&self, f: impl Fn(T) -> U + Copy) -> RangeAny<U>
124    where
125        T: Copy,
126    {
127        RangeAny::new(self.start().map(f), self.end().map(f))
128    }
129
130    /// Returns the intersection of two ranges, if it is non-empty
131    pub fn intersect(&self, other: Self) -> Option<Self>
132    where
133        T: Copy + Ord,
134    {
135        // TODO: https://github.com/rust-lang/rust/issues/144273
136        // let start = a.start.reduce(b.start, std::cmp::max);
137        // let end = a.end.reduce(b.end, std::cmp::min);
138        let start = match (self.start(), other.start()) {
139            (None, None) => None,
140            (None, Some(b)) => Some(b),
141            (Some(a), None) => Some(a),
142            (Some(a), Some(b)) => Some(a.max(b)),
143        };
144        let end = match (self.end(), other.end()) {
145            (None, None) => None,
146            (None, Some(b)) => Some(b),
147            (Some(a), None) => Some(a),
148            (Some(a), Some(b)) => Some(a.min(b)),
149        };
150        if start
151            .as_ref()
152            .is_none_or(|start| end.as_ref().is_none_or(|end| start < end))
153        {
154            Some(Self::new(start, end))
155        } else {
156            // `max()..min()` producing a "backwards" range means the intersection is empty
157            None
158        }
159    }
160}
161
162impl<T> From<Range<T>> for RangeAny<T> {
163    fn from(value: Range<T>) -> Self {
164        Self::new(Some(value.start), Some(value.end))
165    }
166}
167
168/// A marker to make callers acknowledge that a method computes 32-bit offsets or lengths,
169/// and trying to compute past `u32::MAX` code units may result in integer overflow.
170///
171/// The default Rust behavior for integer overflow is panic on debug mode,
172/// and silent wrapping (which for offsets or lengths returns a wrong value) in release mode.
173pub struct AssumeUnder4GB;
174
175fn infallible_u32_to_usize(value: u32) -> usize {
176    const _: () = assert!(usize::BITS >= u32::BITS, "16-bit targets are not supported");
177    value as usize
178}
179
180macro_rules! unicode_length_type {
181    ($( #[$doc:meta] )+ $type_name:ident) => {
182        $( #[$doc] )+
183        #[derive(Clone, Copy, Default, Eq, MallocSizeOf, Ord, PartialEq, PartialOrd)]
184        pub struct $type_name(pub u32);
185
186        impl $type_name {
187            const ZERO: Self = Self(0);
188
189            #[inline]
190            pub fn saturating_sub(self, value: Self) -> Self {
191                Self(self.0.saturating_sub(value.0))
192            }
193
194            #[inline]
195            pub fn to_usize_range(range: &Range<Self>) -> Range<usize> {
196                usize::from(range.start)..usize::from(range.end)
197            }
198        }
199
200        impl From<u32> for $type_name {
201            #[inline]
202            fn from(value: u32) -> Self {
203                Self(value)
204            }
205        }
206
207        impl From<$type_name> for usize {
208            #[inline]
209            fn from(value: $type_name) -> usize {
210                infallible_u32_to_usize(value.0)
211            }
212        }
213
214        impl Add for $type_name {
215            type Output = Self;
216
217            #[inline]
218            fn add(self, other: Self) -> Self {
219                Self(self.0 + other.0)
220            }
221        }
222
223        impl AddAssign for $type_name {
224            #[inline]
225            fn add_assign(&mut self, other: Self) {
226                *self = Self(self.0 + other.0)
227            }
228        }
229
230        impl Sub for $type_name {
231            type Output = Self;
232
233            #[inline]
234            fn sub(self, value: Self) -> Self {
235                Self(self.0 - value.0)
236            }
237        }
238
239        impl SubAssign for $type_name {
240            #[inline]
241            fn sub_assign(&mut self, other: Self) {
242                *self = Self(self.0 - other.0)
243            }
244        }
245
246        impl Sum for $type_name {
247            #[inline]
248            fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
249                iter.fold(Self::ZERO, |a, b| Self(a.0 + b.0))
250            }
251        }
252
253        /// Use compact formatting regardless of `Formatter::alternate`
254        impl fmt::Debug for $type_name {
255            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
256                write!(f, concat!(stringify!($type_name), "({:?})"), self.0)
257            }
258        }
259    };
260}
261
262unicode_length_type! {
263    /// A length or offset counted in 8-bit code units (bytes) in an UTF-8 string.
264    /// This type is used to more reliable work with lengths or offsets in different encodings.
265    Utf8CodeUnits
266}
267
268unicode_length_type! {
269    /// A length or offset counted in 16-bit code units in an UTF-16 string.
270    /// This type is used to more reliable work with lengths or offsets in different encodings.
271    Utf16CodeUnits
272}
273
274unicode_length_type! {
275    /// A length or offset counted in 32-bit code units in UTF-32.
276    /// This is the same as counting Rust `char`s, Unicode scalar values, or Unicode code points.
277    /// This type is used to more reliable work with lengths or offsets in different encodings.
278    Utf32CodeUnits
279}
280
281unicode_length_type! {
282    /// A length or offset counted in 32-bit code units in UTF-32 or a node offset in a container
283    /// node counted in previous siblings.
284    Utf32CodeUnitsOrNodeOffset
285}
286
287impl Utf8CodeUnits {
288    /// Returns the length of `string` in UTF-8 code units (bytes)
289    pub fn length_of(_: AssumeUnder4GB, string: &str) -> Self {
290        Self(string.len() as u32)
291    }
292
293    pub fn length_of_char(char: char) -> Self {
294        // Never overflows, the value is always in 1..=4
295        Self(char.len_utf8() as u32)
296    }
297}
298
299impl Utf16CodeUnits {
300    /// Returns the length of `string` in UTF-16 code units
301    pub fn length_of(_: AssumeUnder4GB, string: &str) -> Self {
302        Self(string.bytes().map(len_utf16_for_utf8_byte).sum())
303
304        // TODO: after upgrading to a Rust version (1.99?) that includes that PR,
305        // replace the above with:
306
307        // // `EncodeUtf16::count` is optimized in https://github.com/rust-lang/rust/pull/159467
308        // Self(string.encode_utf16().count())
309    }
310
311    pub fn length_of_char(char: char) -> Self {
312        // Never overflows, the value is always in 1 or 2
313        Self(char.len_utf16() as u32)
314    }
315
316    /// Convert this UTF-16 offset in `string` to an UTF-8 (byte) offset
317    pub fn to_utf8_code_units_in(self, _: AssumeUnder4GB, string: &str) -> Utf8CodeUnits {
318        self.to_utf8_code_units_in_iter(AssumeUnder4GB, std::iter::once(string))
319    }
320
321    /// Convert this UTF-16 offset in an iterator of strings, to an UTF-8 (byte) offset
322    pub fn to_utf8_code_units_in_iter<S>(
323        self,
324        _: AssumeUnder4GB,
325        iter: impl IntoIterator<Item = S>,
326    ) -> Utf8CodeUnits
327    where
328        S: AsRef<str>,
329    {
330        let mut current_utf16_offset = Utf16CodeUnits(0);
331        let mut current_utf8_offset = Utf8CodeUnits(0);
332        for string in iter {
333            for utf8_byte in string.as_ref().bytes() {
334                current_utf16_offset.0 += len_utf16_for_utf8_byte(utf8_byte);
335                if current_utf16_offset > self {
336                    return current_utf8_offset;
337                }
338                current_utf8_offset.0 += len_utf8_for_utf8_byte(utf8_byte);
339            }
340        }
341        current_utf8_offset
342    }
343
344    /// Convert this UTF-16 offset in `string` to an UTF-32 offset
345    ///
346    /// Note: this never overflows since the return value is always less than or equal to self as
347    /// one UTF-32 code unit corresponds to one or two UTF-16 code units.
348    pub fn to_utf32_code_units_in(self, string: &str) -> Utf32CodeUnits {
349        let mut current_utf16_offset = Utf16CodeUnits(0);
350        let mut current_utf32_offset = Utf32CodeUnits(0);
351        for utf8_byte in string.bytes() {
352            let len_utf16 = len_utf16_for_utf8_byte(utf8_byte);
353            current_utf16_offset.0 += len_utf16;
354            if current_utf16_offset > self {
355                break;
356            }
357            current_utf32_offset.0 += len_utf16_to_len_utf32_for_utf8_byte(len_utf16);
358        }
359        current_utf32_offset
360    }
361}
362
363fn len_utf16_for_utf8_byte(byte: u8) -> u32 {
364    if byte < 0b1000_0000 {
365        // 0b0xxx_xxxx: ASCII-compatible U+0000 to U+007F
366        1
367    } else if byte < 0b1100_0000 {
368        // 0b10xx_xxxx: UTF-8 continuation byte, already accounted for by its non-continuation byte
369        0
370    } else if byte < 0b1111_0000 {
371        // 0b110x_xxxx: start of a 2-byte UTF-8 sequence for U+0080 to U+07FF
372        // 0b1110_xxxx: start of a 3-byte UTF-8 sequence for U+0800 to U+FFFF
373        1
374    } else {
375        // 0b1111_0xxx: start of a 4-byte UTF-8 sequence for U+010000 to U+10FFFF
376        // This is exactly the range encoded as a surrogate pair in UTF-16
377        //
378        // 0b1111_1xxx: would fall here but never occurs in valid UTF-8
379        2
380    }
381}
382
383fn len_utf8_for_utf8_byte(byte: u8) -> u32 {
384    if byte < 0b1000_0000 {
385        // 0b0xxx_xxxx: ASCII-compatible U+0000 to U+007F
386        1
387    } else if byte < 0b1100_0000 {
388        // 0b10xx_xxxx: UTF-8 continuation byte, already accounted for by its non-continuation byte
389        0
390    } else if byte < 0b1110_0000 {
391        // 0b110x_xxxx: start of a 2-byte UTF-8 sequence for U+0080 to U+07FF
392        2
393    } else if byte < 0b1111_0000 {
394        // 0b1110_xxxx: start of a 3-byte UTF-8 sequence for U+0800 to U+FFFF
395        3
396    } else {
397        // 0b1111_0xxx: start of a 4-byte UTF-8 sequence for U+010000 to U+10FFFF
398        4
399    }
400}
401
402fn len_utf16_to_len_utf32_for_utf8_byte(len_utf16: u32) -> u32 {
403    if len_utf16 != 0 {
404        // First byte of the UTF-8 byte sequence for one code point / UTF-32 code unit
405        1
406    } else {
407        // UTF-8 continuation byte
408        0
409    }
410}
411
412impl Utf32CodeUnits {
413    /// Returns the length of `string` in UTF-32 code units (`char` count)
414    pub fn length_of(_: AssumeUnder4GB, string: &str) -> Self {
415        // `std::str::Chars::count` is optimized in:
416        // https://github.com/rust-lang/rust/blob/main/library/core/src/str/count.rs
417        Self(string.chars().count() as u32)
418    }
419
420    /// Convert this UTF-32 (`char`) offset in `string` to an UTF-8 (byte) offset
421    pub fn to_utf8_code_units_in(self, _: AssumeUnder4GB, string: &str) -> Utf8CodeUnits {
422        let mut current_utf32_offset = Utf32CodeUnits(0);
423        for (current_utf8_offset, utf8_byte) in string.bytes().enumerate() {
424            if (utf8_byte & 0b1100_0000) == 0b1000_0000 {
425                // UTF-8 continuation byte
426                continue;
427            }
428            if current_utf32_offset >= self {
429                return Utf8CodeUnits(current_utf8_offset as u32);
430            }
431            current_utf32_offset.0 += 1;
432        }
433        Utf8CodeUnits(string.len() as u32)
434    }
435
436    /// Convert this UTF-32 (`char`) offset in `string` to an UTF-16 offset
437    pub fn to_utf16_code_units_in(self, _: AssumeUnder4GB, string: &str) -> Utf16CodeUnits {
438        let mut current_utf32_offset = Utf32CodeUnits(0);
439        let mut current_utf16_offset = Utf16CodeUnits(0);
440        for utf8_byte in string.bytes() {
441            if current_utf32_offset >= self {
442                break;
443            }
444            let len_utf16 = len_utf16_for_utf8_byte(utf8_byte);
445            current_utf16_offset.0 += len_utf16;
446            current_utf32_offset.0 += len_utf16_to_len_utf32_for_utf8_byte(len_utf16);
447        }
448        current_utf16_offset
449    }
450}
451
452impl Utf32CodeUnitsOrNodeOffset {
453    /// Convert this UTF-32 (`char`) offset in `string` to an UTF-16 offset
454    pub fn to_utf16_code_units_in(self, _: AssumeUnder4GB, string: &str) -> Utf16CodeUnits {
455        Utf32CodeUnits(self.0).to_utf16_code_units_in(AssumeUnder4GB, string)
456    }
457}
458
459#[cfg(test)]
460mod test {
461    use super::*;
462
463    #[test]
464    fn test_is_cjk() {
465        // Test characters from different CJK blocks
466        assert_eq!(is_cjk('〇'), true);
467        assert_eq!(is_cjk('㐀'), true);
468        assert_eq!(is_cjk('あ'), true);
469        assert_eq!(is_cjk('ア'), true);
470        assert_eq!(is_cjk('㆒'), true);
471        assert_eq!(is_cjk('ㆣ'), true);
472        assert_eq!(is_cjk('龥'), true);
473        assert_eq!(is_cjk('𰾑'), true);
474        assert_eq!(is_cjk('𰻝'), true);
475
476        // Test characters from outside CJK blocks
477        assert_eq!(is_cjk('a'), false);
478        assert_eq!(is_cjk('🙂'), false);
479        assert_eq!(is_cjk('©'), false);
480    }
481
482    #[test]
483    fn test_utf16_length() {
484        assert_eq!(
485            Utf16CodeUnits::length_of(AssumeUnder4GB, ""),
486            Utf16CodeUnits(0)
487        );
488        assert_eq!(
489            Utf16CodeUnits::length_of(AssumeUnder4GB, "a"),
490            Utf16CodeUnits(1)
491        );
492        assert_eq!(
493            Utf16CodeUnits::length_of(AssumeUnder4GB, "é"),
494            Utf16CodeUnits(1)
495        );
496        assert_eq!(
497            Utf16CodeUnits::length_of(AssumeUnder4GB, "字"),
498            Utf16CodeUnits(1)
499        );
500        assert_eq!(
501            Utf16CodeUnits::length_of(AssumeUnder4GB, "\u{1F4A9}"),
502            Utf16CodeUnits(2)
503        );
504        assert_eq!(
505            Utf16CodeUnits::length_of(AssumeUnder4GB, "\u{1F4A9}字éa"),
506            Utf16CodeUnits(5)
507        );
508    }
509
510    #[test]
511    fn test_utf16_to_utf32() {
512        let s = "aé字\u{1F4A9}";
513        assert_eq!(
514            Utf16CodeUnits(0).to_utf32_code_units_in(s),
515            Utf32CodeUnits(0)
516        );
517        assert_eq!(
518            Utf16CodeUnits(1).to_utf32_code_units_in(s),
519            Utf32CodeUnits(1)
520        );
521        assert_eq!(
522            Utf16CodeUnits(2).to_utf32_code_units_in(s),
523            Utf32CodeUnits(2)
524        );
525        assert_eq!(
526            Utf16CodeUnits(3).to_utf32_code_units_in(s),
527            Utf32CodeUnits(3)
528        );
529
530        // This 16-bit offset splits the would-be surrogate pair. We return the 32-bit position
531        // before the whole pair. Should this be an error instead?
532        assert_eq!(
533            Utf16CodeUnits(4).to_utf32_code_units_in(s),
534            Utf32CodeUnits(3)
535        );
536
537        assert_eq!(
538            Utf16CodeUnits(5).to_utf32_code_units_in(s),
539            Utf32CodeUnits(4)
540        );
541
542        // This 16-bit offset is out of bounds. We clamp to the nearest valid 32-bit offset,
543        // a.k.a the UTF-32 length. Should this be an error instead?
544        assert_eq!(
545            Utf16CodeUnits(6).to_utf32_code_units_in(s),
546            Utf32CodeUnits(4)
547        );
548        assert_eq!(
549            Utf16CodeUnits(7).to_utf32_code_units_in(s),
550            Utf32CodeUnits(4)
551        );
552    }
553
554    #[test]
555    fn test_utf32_to_utf16() {
556        let string = "aé字\u{1F4A9}";
557        assert_eq!(
558            Utf32CodeUnits(0).to_utf16_code_units_in(AssumeUnder4GB, string),
559            Utf16CodeUnits(0),
560        );
561        assert_eq!(
562            Utf32CodeUnits(1).to_utf16_code_units_in(AssumeUnder4GB, string),
563            Utf16CodeUnits(1),
564        );
565        assert_eq!(
566            Utf32CodeUnits(2).to_utf16_code_units_in(AssumeUnder4GB, string),
567            Utf16CodeUnits(2),
568        );
569        assert_eq!(
570            Utf32CodeUnits(3).to_utf16_code_units_in(AssumeUnder4GB, string),
571            Utf16CodeUnits(3),
572        );
573
574        assert_eq!(
575            Utf32CodeUnits(4).to_utf16_code_units_in(AssumeUnder4GB, string),
576            Utf16CodeUnits(5),
577        );
578
579        // This 32-bit offset is out of bounds. We clamp to the nearest valid 16-bit offset,
580        // a.k.a the UTF-16 length. Should this be an error instead?
581        assert_eq!(
582            Utf32CodeUnits(6).to_utf16_code_units_in(AssumeUnder4GB, string),
583            Utf16CodeUnits(5),
584        );
585        assert_eq!(
586            Utf32CodeUnits(1000).to_utf16_code_units_in(AssumeUnder4GB, string),
587            Utf16CodeUnits(5),
588        );
589    }
590}