unicode-width 0.2.3

Determine displayed width of `char` and `str` types according to Unicode Standard Annex #11 rules.
Documentation
// Copyright 2012-2025 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// 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.

// NOTE: The following code was generated by "scripts/unicode.py", do not edit directly
use crate::tables::*;
use crate::width_info::WidthInfo;

/// Returns the [UAX #11](https://www.unicode.org/reports/tr11/) based width of `c` by
/// consulting a multi-level lookup table.
///
/// # Maintenance
/// The tables themselves are autogenerated but this function is hardcoded. You should have
/// nothing to worry about if you re-run `unicode.py` (for example, when updating Unicode.)
/// However, if you change the *actual structure* of the lookup tables (perhaps by editing the
/// `make_tables` function in `unicode.py`) you must ensure that this code reflects those changes.
#[inline]
pub(crate) fn lookup_width(c: char) -> (u8, WidthInfo) {
    let cp = c as usize;

    let t1_offset = WIDTH_ROOT.0[cp >> 13];

    // Each sub-table in WIDTH_MIDDLE is 7 bits, and each stored entry is a byte,
    // so each sub-table is 128 bytes in size.
    // (Sub-tables are selected using the computed offset from the previous table.)
    let t2_offset = WIDTH_MIDDLE.0[usize::from(t1_offset)][cp >> 7 & 0x3F];

    // Each sub-table in WIDTH_LEAVES is 6 bits, but each stored entry is 2 bits.
    // This is accomplished by packing four stored entries into one byte.
    // So each sub-table is 2**(7-2) == 32 bytes in size.
    // Since this is the last table, each entry represents an encoded width.
    let packed_widths = WIDTH_LEAVES.0[usize::from(t2_offset)][cp >> 2 & 0x1F];

    // Extract the packed width
    let width = packed_widths >> (2 * (cp & 0b11)) & 0b11;

    if width < 3 {
        (width, WidthInfo::DEFAULT)
    } else {
        match c {
            '\u{A}' => (1, WidthInfo::LINE_FEED),
            '\u{5DC}' => (1, WidthInfo::HEBREW_LETTER_LAMED),
            '\u{622}'..='\u{882}' => (1, WidthInfo::JOINING_GROUP_ALEF),
            '\u{1780}'..='\u{17AF}' => (1, WidthInfo::KHMER_COENG_ELIGIBLE_LETTER),
            '\u{17D8}' => (3, WidthInfo::DEFAULT),
            '\u{1A10}' => (1, WidthInfo::BUGINESE_LETTER_YA),
            '\u{2D31}'..='\u{2D6F}' => (1, WidthInfo::TIFINAGH_CONSONANT),
            '\u{A4FC}'..='\u{A4FD}' => (1, WidthInfo::LISU_TONE_LETTER_MYA_NA_JEU),
            '\u{FE01}' => (0, WidthInfo::VARIATION_SELECTOR_1_2_OR_3),
            '\u{FE0E}' => (0, WidthInfo::VARIATION_SELECTOR_15),
            '\u{FE0F}' => (0, WidthInfo::VARIATION_SELECTOR_16),
            '\u{10C03}' => (1, WidthInfo::OLD_TURKIC_LETTER_ORKHON_I),
            '\u{16D67}' => (1, WidthInfo::KIRAT_RAI_VOWEL_SIGN_E),
            '\u{16D68}' => (1, WidthInfo::KIRAT_RAI_VOWEL_SIGN_AI),
            '\u{1F1E6}'..='\u{1F1FF}' => (1, WidthInfo::REGIONAL_INDICATOR),
            '\u{1F3FB}'..='\u{1F3FF}' => (2, WidthInfo::EMOJI_MODIFIER),
            _ => (2, WidthInfo::EMOJI_PRESENTATION),
        }
    }
}
/// Returns the [UAX #11](https://www.unicode.org/reports/tr11/) based width of `c` by
/// consulting a multi-level lookup table.
///
/// # Maintenance
/// The tables themselves are autogenerated but this function is hardcoded. You should have
/// nothing to worry about if you re-run `unicode.py` (for example, when updating Unicode.)
/// However, if you change the *actual structure* of the lookup tables (perhaps by editing the
/// `make_tables` function in `unicode.py`) you must ensure that this code reflects those changes.
#[cfg(feature = "cjk")]
#[inline]
pub(crate) fn lookup_width_cjk(c: char) -> (u8, WidthInfo) {
    let cp = c as usize;

    let t1_offset = WIDTH_ROOT_CJK.0[cp >> 13];

    // Each sub-table in WIDTH_MIDDLE is 7 bits, and each stored entry is a byte,
    // so each sub-table is 128 bytes in size.
    // (Sub-tables are selected using the computed offset from the previous table.)
    let t2_offset = WIDTH_MIDDLE.0[usize::from(t1_offset)][cp >> 7 & 0x3F];

    // Each sub-table in WIDTH_LEAVES is 6 bits, but each stored entry is 2 bits.
    // This is accomplished by packing four stored entries into one byte.
    // So each sub-table is 2**(7-2) == 32 bytes in size.
    // Since this is the last table, each entry represents an encoded width.
    let packed_widths = WIDTH_LEAVES.0[usize::from(t2_offset)][cp >> 2 & 0x1F];

    // Extract the packed width
    let width = packed_widths >> (2 * (cp & 0b11)) & 0b11;

    if width < 3 {
        (width, WidthInfo::DEFAULT)
    } else {
        match c {
            '\u{A}' => (1, WidthInfo::LINE_FEED),
            '\u{338}' => (0, WidthInfo::COMBINING_LONG_SOLIDUS_OVERLAY),
            '\u{5DC}' => (1, WidthInfo::HEBREW_LETTER_LAMED),
            '\u{622}'..='\u{882}' => (1, WidthInfo::JOINING_GROUP_ALEF),
            '\u{1780}'..='\u{17AF}' => (1, WidthInfo::KHMER_COENG_ELIGIBLE_LETTER),
            '\u{17D8}' => (3, WidthInfo::DEFAULT),
            '\u{1A10}' => (1, WidthInfo::BUGINESE_LETTER_YA),
            '\u{2D31}'..='\u{2D6F}' => (1, WidthInfo::TIFINAGH_CONSONANT),
            '\u{A4FC}'..='\u{A4FD}' => (1, WidthInfo::LISU_TONE_LETTER_MYA_NA_JEU),
            '\u{FE00}'..='\u{FE02}' => (0, WidthInfo::VARIATION_SELECTOR_1_2_OR_3),
            '\u{FE0F}' => (0, WidthInfo::VARIATION_SELECTOR_16),
            '\u{10C03}' => (1, WidthInfo::OLD_TURKIC_LETTER_ORKHON_I),
            '\u{16D67}' => (1, WidthInfo::KIRAT_RAI_VOWEL_SIGN_E),
            '\u{16D68}' => (1, WidthInfo::KIRAT_RAI_VOWEL_SIGN_AI),
            '\u{1F1E6}'..='\u{1F1FF}' => (1, WidthInfo::REGIONAL_INDICATOR),
            '\u{1F3FB}'..='\u{1F3FF}' => (2, WidthInfo::EMOJI_MODIFIER),
            _ => (2, WidthInfo::EMOJI_PRESENTATION),
        }
    }
}