pub(super) const XX: u8 = 0;
pub(super) const AL: u8 = 1;
pub(super) const AI: u8 = 2;
pub(super) const DOTTED: u8 = 3;
pub(super) const SA: u8 = 4;
pub(super) const SA_MARK: u8 = 5;
pub(super) const BK: u8 = 6;
pub(super) const CR: u8 = 7;
pub(super) const LF: u8 = 8;
pub(super) const NL: u8 = 9;
pub(super) const SP: u8 = 10;
pub(super) const ZW: u8 = 11;
pub(super) const WJ: u8 = 12;
pub(super) const GL: u8 = 13;
pub(super) const CM: u8 = 14;
pub(super) const ZWJ: u8 = 15;
pub(super) const CL: u8 = 16;
pub(super) const CP: u8 = 17;
pub(super) const EX: u8 = 18;
pub(super) const IS: u8 = 19;
pub(super) const SY: u8 = 20;
pub(super) const OP: u8 = 21;
pub(super) const OP_EA: u8 = 22;
pub(super) const QU: u8 = 23;
pub(super) const QU_PI: u8 = 24;
pub(super) const QU_PF: u8 = 25;
pub(super) const NS: u8 = 26;
pub(super) const CJ: u8 = 27;
pub(super) const B2: u8 = 28;
pub(super) const BA: u8 = 29;
pub(super) const BB: u8 = 30;
pub(super) const HY: u8 = 31;
pub(super) const HH: u8 = 32;
pub(super) const CB: u8 = 33;
pub(super) const IN: u8 = 34;
pub(super) const NU: u8 = 35;
pub(super) const PO: u8 = 36;
pub(super) const PR: u8 = 37;
pub(super) const HL: u8 = 38;
pub(super) const ID: u8 = 39;
pub(super) const ID_CN: u8 = 40;
pub(super) const EB: u8 = 41;
pub(super) const EM: u8 = 42;
pub(super) const JL: u8 = 43;
pub(super) const JV: u8 = 44;
pub(super) const JT: u8 = 45;
pub(super) const H2: u8 = 46;
pub(super) const H3: u8 = 47;
pub(super) const RI: u8 = 48;
pub(super) const AK: u8 = 49;
pub(super) const AP: u8 = 50;
pub(super) const AS: u8 = 51;
pub(super) const VF: u8 = 52;
pub(super) const VI: u8 = 53;
pub(super) const PO_EAW: u8 = 54;
pub(super) const PR_EAW: u8 = 55;
pub(super) const NS_DASH: u8 = 56;
pub(super) const NS_ITERATION: u8 = 57;
pub(super) const NS_CENTRED: u8 = 58;
pub(super) const EX_WIDE: u8 = 59;
pub(super) const DATA_CLASS_COUNT: usize = 60;
pub(super) const LINE_BREAK: [u8; DATA_CLASS_COUNT] = {
let mut values = [0; DATA_CLASS_COUNT];
let pairs: [(u8, u8); DATA_CLASS_COUNT] = [
(XX, 0),
(AL, 2),
(AI, 1),
(DOTTED, 2),
(SA, 24),
(SA_MARK, 24),
(BK, 6),
(CR, 10),
(LF, 17),
(NL, 29),
(SP, 26),
(ZW, 28),
(WJ, 30),
(GL, 12),
(CM, 9),
(ZWJ, 42),
(CL, 8),
(CP, 36),
(EX, 11),
(IS, 16),
(SY, 27),
(OP, 20),
(OP_EA, 20),
(QU, 23),
(QU_PI, 23),
(QU_PF, 23),
(NS, 18),
(CJ, 37),
(B2, 3),
(BA, 4),
(BB, 5),
(HY, 13),
(HH, 48),
(CB, 7),
(IN, 15),
(NU, 19),
(PO, 21),
(PR, 22),
(HL, 38),
(ID, 14),
(ID_CN, 14),
(EB, 40),
(EM, 41),
(JL, 33),
(JV, 35),
(JT, 34),
(H2, 31),
(H3, 32),
(RI, 39),
(AK, 43),
(AP, 44),
(AS, 45),
(VF, 46),
(VI, 47),
(PO_EAW, 21),
(PR_EAW, 22),
(NS_DASH, 18),
(NS_ITERATION, 18),
(NS_CENTRED, 18),
(EX_WIDE, 11),
];
let mut i = 0;
while i < DATA_CLASS_COUNT {
let (class, value) = pairs[i];
values[class as usize] = value;
i += 1;
}
values
};
pub(super) const NSX: u8 = 60;
pub(super) const ID_LETTER: u8 = 61;
pub(super) const CLASS_COUNT: usize = 62;
pub(super) const SOT: u8 = 62;
pub(super) const SP_ZW: u8 = 63;
pub(super) const SP_OP: u8 = 64;
pub(super) const SP_QU: u8 = 65;
pub(super) const SP_CLCP: u8 = 66;
pub(super) const SP_B2: u8 = 67;
pub(super) const QU_PI_START: u8 = 68;
pub(super) const HY_WORD: u8 = 69;
pub(super) const HH_WORD: u8 = 70;
pub(super) const HY_HL: u8 = 71;
pub(super) const HH_HL: u8 = 72;
pub(super) const SY_NU: u8 = 73;
pub(super) const IS_NU: u8 = 74;
pub(super) const CL_NU: u8 = 75;
pub(super) const CP_NU: u8 = 76;
pub(super) const VI_AK: u8 = 77;
pub(super) const RI_EVEN: u8 = 78;
#[cfg(test)]
pub(super) const BA_HL: u8 = 79;
#[cfg(test)]
pub(super) const MARK_AL: u8 = 80;
#[cfg(test)]
pub(super) const DOTTED_PAIR: u8 = 81;
type Set = u128;
const _: () = assert!(
DATA_CLASS_COUNT <= 64 && CLASS_COUNT <= 64 && RI_EVEN < EOT && EOT < UNREAD && UNREAD < 128
);
#[cfg(test)]
const _: () = assert!(DOTTED_PAIR < EOT);
const fn set(members: &[u8]) -> Set {
let mut bits = 0;
let mut at = 0;
while at < members.len() {
bits |= 1 << members[at];
at += 1;
}
bits
}
#[inline(always)]
const fn is(class: u8, set: Set) -> bool {
(set >> (class & 127)) & 1 != 0
}
const HARD: Set = set(&[BK, CR, LF, NL]);
pub(super) const SPACES: Set = set(&[SP, SP_ZW, SP_OP, SP_QU, SP_CLCP, SP_B2]);
const UNMARKABLE: Set = set(&[
SOT, BK, CR, LF, NL, ZW, SP, SP_ZW, SP_OP, SP_QU, SP_CLCP, SP_B2,
]);
#[inline(always)]
pub(super) const fn is_unmarkable(state: u8) -> bool {
is(state, UNMARKABLE)
}
const QUOTES: Set = set(&[QU, QU_PI, QU_PF, QU_PI_START]);
const OPENS: Set = set(&[OP, OP_EA]);
const HYPHENS: Set = set(&[HY, HY_WORD, HY_HL]);
const HARD_HYPHENS: Set = set(&[HH, HH_WORD, HH_HL]);
const ALPHABETIC: Set = set(&[AL, AI, XX, SA, DOTTED]) | MARKS_AS_AL;
#[cfg(test)]
const MARKS_AS_AL: Set = set(&[MARK_AL]);
#[cfg(not(test))]
const MARKS_AS_AL: Set = 0;
const LETTERS: Set = ALPHABETIC | set(&[HL]);
const POSTFIXES: Set = set(&[PO, PO_EAW]);
const PREFIXES: Set = set(&[PR, PR_EAW]);
const AFFIXES: Set = POSTFIXES | PREFIXES;
const AFFIXES_BESIDE_LETTERS: Set = set(&[PR, PO, PO_EAW]);
const AFFIXES_AFTER_NUMBERS: Set = set(&[PR, PR_EAW, PO]);
const IDEOGRAPHS: Set = set(&[ID, ID_CN, ID_LETTER, EB, EM]);
const NONSTARTERS: Set = set(&[NS, CJ]);
const KOREAN: Set = set(&[JL, JV, JT, H2, H3]);
const AKSARA: Set = set(&[AK, DOTTED, AS]);
const NUMBER: Set = set(&[NU, SY_NU, IS_NU]);
const CLOSES: Set = set(&[CL, CP, CL_NU, CP_NU]);
const QUOTE_FOLLOWERS: Set = set(&[
SP, GL, WJ, CL, QU, QU_PI, QU_PF, CP, EX, IS, SY, BK, CR, LF, NL, ZW, EOT,
]);
const QUOTE_OPENERS: Set = set(&[SOT, BK, CR, LF, NL, OP, OP_EA, GL, ZW]) | QUOTES | SPACES;
const WORD_STARTS: Set = set(&[SOT, BK, CR, LF, NL, ZW, CB, GL]) | SPACES;
const KEEP_ALL: Set = set(&[AI, AL, ID, NU, H2, H3, JL, JV, JT, CJ]);
#[cfg(test)]
const ICU_KEEP_ALL: Set = KEEP_ALL | set(&[HY, HY_WORD, HY_HL]);
const LOOSE_HYPHEN_BASES: Set = set(&[ID, ID_CN, ID_LETTER]);
pub(super) const EOT: u8 = 126;
pub(super) const UNREAD: u8 = 127;
pub(super) const KEEP: u8 = 0;
pub(super) const BREAK: u8 = 1;
pub(super) const NEEDS_NEXT: u8 = 2;
pub(super) const NEEDS_SECOND: u8 = 3;
const NEEDS_FLAGS: u8 = 4;
pub(super) const LEFT_EAST_ASIAN: u8 = 1;
pub(super) const BEFORE_EAST_ASIAN: u8 = 2;
pub(super) const RIGHT_EAST_ASIAN: u8 = 4;
pub(super) const AFTER_ZWJ: u8 = 8;
pub(super) const NEXT_EAST_ASIAN: u8 = 16;
#[cfg(test)]
pub(super) const ICU_INNER: u8 = 32;
#[cfg(test)]
pub(super) const ICU_INNER_BEFORE_FOLLOWER: u8 = 64;
pub(super) const LEFT_UNASSIGNED_PICTOGRAPH: u8 = 128;
#[inline(always)]
pub(super) const fn left_flags(bits: u8) -> u8 {
((bits >> 6) & 1) * LEFT_EAST_ASIAN + (bits >> 7) * LEFT_UNASSIGNED_PICTOGRAPH
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub(super) enum Tailoring {
None,
NormalJapaneseChinese,
Loose,
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub(super) struct Rules {
pub(super) tailoring: Tailoring,
pub(super) ja_zh: bool,
pub(super) keep_all: bool,
pub(super) loose_hyphens: bool,
#[cfg(test)]
pub(super) legacy: u32,
}
pub(super) struct Legacy;
impl Legacy {
pub(super) const LB12A: u32 = 1;
pub(super) const LB13: u32 = 2;
pub(super) const LB19: u32 = 4;
pub(super) const LB20A: u32 = 8;
pub(super) const LB21A: u32 = 16;
pub(super) const LB25: u32 = 32;
#[cfg(test)]
pub(super) const UNICODE_15_1: u32 =
Self::LB12A | Self::LB13 | Self::LB19 | Self::LB20A | Self::LB21A | Self::LB25;
#[cfg(test)]
pub(super) const LOOSE_HOLDS: u32 = 64;
pub(super) const TAILORING_FIRST: u32 = 128;
#[cfg(test)]
pub(super) const SA_MARKS: u32 = 256;
#[cfg(test)]
pub(super) const MARKS_APART: u32 = 512;
#[cfg(test)]
pub(super) const TAILORING_OUTSIDE_RULES: u32 = 1024;
pub(super) const DOTTED_PAIRS: u32 = 2048;
#[cfg(test)]
pub(super) const CONTEXT_FORGOTTEN: u32 = 4096;
pub(super) const ICU_TAILORING: u32 = 8192;
pub(super) const KEEP_ALL_HYPHENS: u32 = 16384;
#[cfg(test)]
pub(super) const RUN_END_BREAKS: u32 = 1 << 15;
#[cfg(test)]
pub(super) const LAO_LO_LING: u32 = 1 << 16;
#[cfg(test)]
pub(super) const DICTIONARY_LEAVES: u32 = 1 << 17;
#[cfg(test)]
pub(super) const DICTIONARY_MISSES: u32 = 1 << 18;
#[cfg(test)]
pub(super) const DICTIONARY_REST: u32 = 1 << 19;
#[cfg(test)]
pub(super) const RUN_MODEL: u32 = 1 << 20;
#[cfg(test)]
pub(super) const DICTIONARY_RUNS: u32 = Self::RUN_MODEL
| Self::RUN_END_BREAKS
| Self::LAO_LO_LING
| Self::DICTIONARY_LEAVES
| Self::DICTIONARY_MISSES
| Self::DICTIONARY_REST;
}
#[inline(always)]
const fn legacy(rules: &Rules, bits: u32) -> bool {
#[cfg(test)]
{
rules.legacy & bits != 0
}
#[cfg(not(test))]
{
let _ = (rules, bits);
false
}
}
#[derive(Copy, Clone, Debug)]
pub(super) struct Pair {
pub(super) left: u8,
pub(super) right: u8,
pub(super) flags: u8,
pub(super) flags_unread: bool,
pub(super) ch: char,
pub(super) next: u8,
pub(super) second: u8,
}
pub(super) const fn decide(pair: &Pair, rules: &Rules) -> u8 {
let (l, r) = (pair.left, pair.right);
if legacy(rules, Legacy::TAILORING_FIRST) {
let first = icu_first(pair, rules);
if first != UNREAD {
return first;
}
}
if l == BK || l == LF || l == NL {
return BREAK;
}
if l == CR {
return if r == LF { KEEP } else { BREAK };
}
if is(r, HARD) || r == SP || r == ZW {
return KEEP;
}
if l == ZW || l == SP_ZW {
return BREAK;
}
if pair.flags & AFTER_ZWJ != 0 {
return KEEP;
}
if l == WJ || r == WJ || l == GL {
return KEEP;
}
if !legacy(rules, Legacy::TAILORING_FIRST) && !icu_inner(pair, rules) {
let tailored = tailor(pair, rules);
if tailored != UNREAD {
return tailored;
}
}
if r == GL {
let breaks_before = if legacy(rules, Legacy::LB12A) {
SPACES | HYPHENS | set(&[BA])
} else {
SPACES | HYPHENS | HARD_HYPHENS
};
if !is(l, breaks_before) {
return KEEP;
}
}
if r == CL || r == CP || r == EX || r == SY {
return KEEP;
}
if legacy(rules, Legacy::LB13) && r == IS {
return KEEP;
}
if is(l, OPENS) || l == SP_OP {
return KEEP;
}
if l == QU_PI_START || l == SP_QU {
return KEEP;
}
if r == QU_PF {
if pair.next == UNREAD {
return NEEDS_NEXT;
}
if is(pair.next, QUOTE_FOLLOWERS) {
return KEEP;
}
}
if r == IS {
if is(l, SPACES) {
if pair.next == UNREAD {
return NEEDS_NEXT;
}
if pair.next == NU {
return BREAK;
}
}
return KEEP;
}
if (is(l, CLOSES) || l == SP_CLCP) && is(r, NONSTARTERS) {
return KEEP;
}
if (l == B2 || l == SP_B2) && r == B2 {
return KEEP;
}
if is(l, SPACES) {
return BREAK;
}
if legacy(rules, Legacy::LB19) {
if is(l, QUOTES) || is(r, QUOTES) {
return KEEP;
}
} else {
if r == QU || r == QU_PF || l == QU || l == QU_PI || l == QU_PI_START {
return KEEP;
}
if pair.flags_unread && (is(r, QUOTES) || is(l, QUOTES)) {
return NEEDS_FLAGS;
}
if is(r, QUOTES) {
if pair.flags & LEFT_EAST_ASIAN == 0 {
return KEEP;
}
if pair.next == UNREAD {
return NEEDS_NEXT;
}
if pair.next == EOT || pair.flags & NEXT_EAST_ASIAN == 0 {
return KEEP;
}
}
if is(l, QUOTES)
&& (pair.flags & RIGHT_EAST_ASIAN == 0 || pair.flags & BEFORE_EAST_ASIAN == 0)
{
return KEEP;
}
}
if l == CB || r == CB {
return BREAK;
}
if !legacy(rules, Legacy::LB20A)
&& (l == HY_WORD || l == HH_WORD)
&& (is(r, LETTERS) || r == ID_LETTER)
{
return KEEP;
}
if r == BA || r == HH || r == HY || is(r, NONSTARTERS) || l == BB {
return KEEP;
}
if legacy(rules, Legacy::LB21A) {
if l == HY_HL || l == HH_HL || is_ba_after_hl(l) {
return KEEP;
}
} else if (l == HY_HL || l == HH_HL) && r != HL {
return KEEP;
}
if (l == SY || l == SY_NU) && r == HL {
return KEEP;
}
if r == IN {
return KEEP;
}
if (is(l, LETTERS) && r == NU) || (l == NU && is(r, LETTERS)) {
return KEEP;
}
let icu = legacy(rules, Legacy::ICU_TAILORING);
let (prefixes, postfixes) = if icu {
(PREFIXES, POSTFIXES)
} else {
(set(&[PR]), set(&[PO]))
};
let (beside_letters, after_numbers) = if icu {
(AFFIXES, AFFIXES)
} else {
(AFFIXES_BESIDE_LETTERS, AFFIXES_AFTER_NUMBERS)
};
if (is(l, prefixes) && is(r, IDEOGRAPHS)) || (is(l, IDEOGRAPHS) && is(r, postfixes)) {
return KEEP;
}
if (is(l, beside_letters) && is(r, LETTERS)) || (is(l, LETTERS) && is(r, beside_letters)) {
return KEEP;
}
if legacy(rules, Legacy::LB25) {
if numbers_15_1(l, r) {
return KEEP;
}
} else {
if (l == CL_NU || l == CP_NU) && is(r, after_numbers) {
return KEEP;
}
if is(l, NUMBER) && (is(r, after_numbers) || r == NU) {
return KEEP;
}
if is(l, beside_letters) {
if r == NU {
return KEEP;
}
if is(r, OPENS) {
if pair.next == UNREAD {
return NEEDS_NEXT;
}
if pair.next == NU {
return KEEP;
}
if pair.next == IS {
if pair.second == UNREAD {
return NEEDS_SECOND;
}
if pair.second == NU {
return KEEP;
}
}
}
}
if (is(l, HYPHENS) || l == IS || l == IS_NU) && r == NU {
return KEEP;
}
}
if (l == JL && (r == JL || r == JV || r == H2 || r == H3))
|| ((l == JV || l == H2) && (r == JV || r == JT))
|| ((l == JT || l == H3) && r == JT)
{
return KEEP;
}
if (is(l, KOREAN) && is(r, postfixes)) || (is(l, prefixes) && is(r, KOREAN)) {
return KEEP;
}
if is(l, LETTERS) && is(r, LETTERS) {
return KEEP;
}
if (l == AP && is(r, AKSARA))
|| (is(l, AKSARA) && (r == VF || r == VI))
|| (l == VI_AK && (r == AK || r == DOTTED))
{
return KEEP;
}
if is(l, AKSARA) && is(r, AKSARA) {
if pair.next == UNREAD {
return NEEDS_NEXT;
}
if pair.next == VF {
return KEEP;
}
}
if (l == IS || l == IS_NU) && is(r, LETTERS) {
return KEEP;
}
if ((is(l, LETTERS) || l == NU) && r == OP)
|| ((l == CP || l == CP_NU) && (is(r, LETTERS) || r == NU))
{
return KEEP;
}
if l == RI && r == RI {
return KEEP;
}
if r == EM {
if l == EB {
return KEEP;
}
if pair.flags_unread {
return NEEDS_FLAGS;
}
if pair.flags & LEFT_UNASSIGNED_PICTOGRAPH != 0 {
return KEEP;
}
}
BREAK
}
const fn icu_first(pair: &Pair, rules: &Rules) -> u8 {
if pair.flags & AFTER_ZWJ != 0 {
return KEEP;
}
if icu_inner(pair, rules) {
return dotted_pair(pair, rules);
}
let tailored = tailor(pair, rules);
if tailored != UNREAD {
return tailored;
}
dotted_pair(pair, rules)
}
#[cfg(test)]
pub(super) const fn icu_tailors(pair: &Pair, rules: &Rules) -> bool {
!icu_inner(pair, rules) && tailor(pair, rules) != UNREAD
}
const fn dotted_pair(pair: &Pair, rules: &Rules) -> u8 {
#[cfg(test)]
if legacy(rules, Legacy::DOTTED_PAIRS) && pair.left == DOTTED_PAIR {
let held = set(&[AI, AL, HL, OP, SA, VF, XX, QU_PI]);
return if is(pair.right, held) { KEEP } else { BREAK };
}
let _ = (pair, rules);
UNREAD
}
#[inline(always)]
const fn icu_inner(pair: &Pair, rules: &Rules) -> bool {
#[cfg(test)]
{
legacy(rules, Legacy::TAILORING_OUTSIDE_RULES)
&& (pair.flags & ICU_INNER != 0
|| (pair.flags & ICU_INNER_BEFORE_FOLLOWER != 0 && is(pair.right, QUOTE_FOLLOWERS)))
}
#[cfg(not(test))]
{
let _ = (pair, rules);
false
}
}
#[cfg(test)]
pub(super) const fn icu_inner_flags(left: u8, right: u8, next: u8) -> u8 {
let inner = set(&[
SP_OP,
SP_QU,
SP_CLCP,
SP_B2,
SP_ZW,
QU_PI,
HY_HL,
HH_HL,
BA_HL,
RI_EVEN,
VI_AK,
DOTTED_PAIR,
]);
if is(next, inner) || (right == QU_PF && (left == SP_OP || left == CB)) {
ICU_INNER
} else if right == QU_PF && is(left, SPACES) {
ICU_INNER_BEFORE_FOLLOWER
} else {
0
}
}
#[inline(always)]
const fn is_ba_after_hl(left: u8) -> bool {
#[cfg(test)]
{
left == BA_HL
}
#[cfg(not(test))]
{
let _ = left;
false
}
}
const fn numbers_15_1(left: u8, right: u8) -> bool {
let closes = is(left, CLOSES);
let numbers = left == NU;
let affixes = is(left, AFFIXES);
((closes || numbers) && is(right, AFFIXES))
|| (affixes && (is(right, OPENS) || right == NU))
|| ((is(left, HYPHENS)
|| left == IS
|| left == IS_NU
|| numbers
|| left == SY
|| left == SY_NU)
&& right == NU)
}
const fn tailor(pair: &Pair, rules: &Rules) -> u8 {
let (l, r) = (pair.left, pair.right);
let keep_all = if legacy(rules, Legacy::KEEP_ALL_HYPHENS) {
icu_keep_all()
} else {
KEEP_ALL
};
if rules.keep_all && is(l, keep_all) && is(r, keep_all) {
return KEEP;
}
if legacy(rules, Legacy::ICU_TAILORING) {
return icu_tailor(pair, rules);
}
if matches!(rules.tailoring, Tailoring::Loose)
&& ((l == IN && r == IN)
|| (rules.loose_hyphens && is(l, LOOSE_HYPHEN_BASES) && is_loose_hyphen(pair.ch)))
{
return BREAK;
}
UNREAD
}
#[inline(always)]
const fn is_loose_hyphen(ch: char) -> bool {
matches!(ch, '\u{2010}' | '\u{2013}')
}
#[inline(always)]
const fn icu_keep_all() -> Set {
#[cfg(test)]
{
ICU_KEEP_ALL
}
#[cfg(not(test))]
{
KEEP_ALL
}
}
const fn icu_tailor(pair: &Pair, rules: &Rules) -> u8 {
#[cfg(test)]
{
let (l, r) = (pair.left, pair.right);
let ch = pair.ch as u32;
let off = if legacy(rules, Legacy::LOOSE_HOLDS) {
KEEP
} else {
UNREAD
};
let ja_zh = if rules.ja_zh { BREAK } else { off };
match rules.tailoring {
Tailoring::None => UNREAD,
Tailoring::NormalJapaneseChinese => {
if ch == 0x301C || ch == 0x30A0 {
BREAK
} else {
UNREAD
}
}
Tailoring::Loose => {
let always = ((ch == 0x2010 || ch == 0x2013) && (l == ID || l == ID_LETTER))
|| (r == NS
&& matches!(ch, 0x3005 | 0x303B | 0x309D | 0x309E | 0x30FD | 0x30FE))
|| (r == IN && l == IN);
let japanese_chinese = (r == NS
&& matches!(
ch,
0x301C | 0x30A0 | 0x30FB | 0xFF1A | 0xFF1B | 0xFF65 | 0x203C | 0x2047
..=0x2049
))
|| (r == EX && (ch == 0xFF01 || ch == 0xFF1F))
|| r == PO_EAW
|| l == PR_EAW;
if always {
BREAK
} else if japanese_chinese {
ja_zh
} else {
UNREAD
}
}
}
}
#[cfg(not(test))]
{
let _ = (pair, rules);
UNREAD
}
}
pub(super) const fn next_left(left: u8, right: u8, ch: char, rules: &Rules) -> u8 {
match right {
SP => {
if left == ZW || left == SP_ZW {
SP_ZW
} else if is(left, OPENS) || left == SP_OP {
SP_OP
} else if left == QU_PI_START || left == SP_QU {
SP_QU
} else if is(left, CLOSES) || left == SP_CLCP {
SP_CLCP
} else if left == B2 || left == SP_B2 {
SP_B2
} else {
SP
}
}
QU_PI if is(left, QUOTE_OPENERS) => QU_PI_START,
HY | HH => {
let word = is(left, WORD_STARTS)
|| (rules.loose_hyphens
&& !legacy(rules, Legacy::ICU_TAILORING)
&& is(left, LOOSE_HYPHEN_BASES)
&& is_loose_hyphen(ch));
match (right, word, left == HL) {
(HY, true, _) => HY_WORD,
(HY, _, true) => HY_HL,
(HH, true, _) => HH_WORD,
(HH, _, true) => HH_HL,
_ => right,
}
}
SY if is(left, NUMBER) => SY_NU,
IS if is(left, NUMBER) => IS_NU,
CL if is(left, NUMBER) => CL_NU,
CP if is(left, NUMBER) => CP_NU,
VI if is(left, AKSARA) => VI_AK,
RI if left == RI => RI_EVEN,
BA if left == HL && legacy(rules, Legacy::LB21A) => ba_after_hl(),
DOTTED if left == DOTTED && legacy(rules, Legacy::DOTTED_PAIRS) => dotted_after_dotted(),
_ => right,
}
}
#[inline(always)]
const fn ba_after_hl() -> u8 {
#[cfg(test)]
{
BA_HL
}
#[cfg(not(test))]
{
BA
}
}
#[inline(always)]
const fn dotted_after_dotted() -> u8 {
#[cfg(test)]
{
DOTTED_PAIR
}
#[cfg(not(test))]
{
DOTTED
}
}
pub(super) const RESOLVE_STRICTNESSES: usize = 5;
const fn resolve(raw: u8, strictness: usize, break_all: bool, sa_marks: bool, icu: bool) -> u8 {
let cj_as_id = strictness != 0;
let normal_ja_zh = strictness == 2;
let loose = strictness >= 3;
let loose_ja_zh = strictness == 4;
match raw {
SA_MARK if sa_marks && break_all => ID,
SA_MARK if sa_marks => SA,
SA_MARK => CM,
CJ if cj_as_id || break_all => ID,
AL if break_all => ID_LETTER,
NU | SA if break_all => ID,
NS_DASH if !icu && (normal_ja_zh || loose_ja_zh) => NSX,
NS_ITERATION if !icu && loose => NSX,
NS_CENTRED | EX_WIDE if !icu && loose_ja_zh => NSX,
NS_DASH | NS_ITERATION | NS_CENTRED => NS,
EX_WIDE => EX,
PO_EAW if !icu && !loose_ja_zh => PO,
PR_EAW if !icu && !loose_ja_zh => PR,
_ => raw,
}
}
const fn resolve_table(strictness: usize, break_all: bool, sa_marks: bool, icu: bool) -> [u8; 64] {
let mut table = [XX; 64];
let mut raw = 0;
while raw < DATA_CLASS_COUNT {
table[raw] = resolve(raw as u8, strictness, break_all, sa_marks, icu);
raw += 1;
}
table
}
const fn resolve_tables(sa_marks: bool, icu: bool) -> [[u8; 64]; 2 * RESOLVE_STRICTNESSES] {
let mut tables = [[XX; 64]; 2 * RESOLVE_STRICTNESSES];
let mut index = 0;
while index < tables.len() {
tables[index] = resolve_table(index / 2, index % 2 == 1, sa_marks, icu);
index += 1;
}
tables
}
pub(super) static RESOLVE: [[u8; 64]; 2 * RESOLVE_STRICTNESSES] = resolve_tables(false, false);
#[cfg(test)]
pub(super) static RESOLVE_ICU: [[[u8; 64]; 2 * RESOLVE_STRICTNESSES]; 4] = [
resolve_tables(false, false),
resolve_tables(true, false),
resolve_tables(false, true),
resolve_tables(true, true),
];
pub(super) const TABLE_STATES: usize = RI_EVEN as usize + 1;
pub(super) const PAIR_BREAKS: u8 = 1;
pub(super) const PAIR_BREAKS_KEEP_ALL: u8 = 2;
pub(super) const PAIR_READS_MORE: u8 = 4;
pub(super) static PAIRS: [[u8; CLASS_COUNT]; TABLE_STATES] = {
let mut table = [[0; CLASS_COUNT]; TABLE_STATES];
let mut left = 0;
while left < TABLE_STATES {
let mut right = 0;
while right < CLASS_COUNT {
table[left][right] = pair_entry(left as u8, right as u8);
right += 1;
}
left += 1;
}
table
};
const fn pair_entry(left: u8, right: u8) -> u8 {
let mut entry = 0;
let mut keep_all = 0;
while keep_all < 2 {
let rules = Rules {
tailoring: Tailoring::None,
ja_zh: false,
keep_all: keep_all == 1,
loose_hyphens: false,
#[cfg(test)]
legacy: 0,
};
let pair = Pair {
left,
right,
flags: 0,
flags_unread: true,
ch: '\0',
next: UNREAD,
second: UNREAD,
};
let answer = decide(&pair, &rules);
if answer != KEEP && answer != BREAK {
return PAIR_READS_MORE;
}
entry |= answer << keep_all;
keep_all += 1;
}
entry
}
#[inline(always)]
pub(super) const fn may_tailor(left: u8, right: u8) -> bool {
(left == IN && right == IN) || (is(left, LOOSE_HYPHEN_BASES) && (right == HH || right == BA))
}