part of dart._engine; // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. const int kChar_0 = 48; const int kChar_9 = kChar_0 + 9; const int kChar_A = 65; const int kChar_Z = 90; const int kChar_a = 97; const int kChar_z = 122; const int kCharBang = 33; const int kMashriqi_0 = 0x660; const int kMashriqi_9 = kMashriqi_0 + 9; enum _ComparisonResult { inside, higher, lower } /// Each instance of [UnicodeRange] represents a range of unicode characters /// that are assigned a [CharProperty]. For example, the following snippet: /// /// ```dart /// UnicodeRange(0x0041, 0x005A, CharProperty.ALetter); /// ``` /// /// is saying that all characters between 0x0041 ("A") and 0x005A ("Z") are /// assigned the property [CharProperty.ALetter]. /// /// Note that the Unicode spec uses inclusive ranges and we are doing the /// same here. class UnicodeRange
{ const UnicodeRange(this.start, this.end, this.property); final int start; final int end; final P property; /// Compare a [value] to this range. /// /// The return value is either: /// - lower: The value is lower than the range. /// - higher: The value is higher than the range /// - inside: The value is within the range. _ComparisonResult compare(int value) { if (value < start) { return _ComparisonResult.lower; } if (value > end) { return _ComparisonResult.higher; } return _ComparisonResult.inside; } } /// Checks whether the given char code is a UTF-16 surrogate. /// /// See: /// - http://www.unicode.org/faq//utf_bom.html#utf16-2 bool isUtf16Surrogate(int char) { return char & 0xF800 == 0xD800; } /// Combines a pair of UTF-16 surrogate into a single character code point. /// /// The surrogate pair is expected to start at [index] in the [text]. /// /// See: /// - http://www.unicode.org/faq//utf_bom.html#utf16-3 int combineSurrogatePair(String text, int index) { final int hi = text.codeUnitAt(index); final int lo = text.codeUnitAt(index + 1); final int x = (hi & ((1 << 6) - 1)) << 10 | lo & ((1 << 10) - 1); final int w = (hi >> 6) & ((1 << 5) - 1); final int u = w + 1; return u << 16 | x; } /// Returns the code point from [text] at [index] and handles surrogate pairs /// for cases that involve two UTF-16 codes. int? getCodePoint(String text, int index) { if (index < 0 || index >= text.length) { return null; } final int char = text.codeUnitAt(index); if (isUtf16Surrogate(char) && index < text.length - 1) { return combineSurrogatePair(text, index); } return char; } /// Given a list of [UnicodeRange]s, this class performs efficient lookup /// to find which range a value falls into. /// /// The lookup algorithm expects the ranges to have the following constraints: /// - Be sorted. /// - No overlap between the ranges. /// - Gaps between ranges are ok. /// /// This is used in the context of unicode to find out what property a letter /// has. The properties are then used to decide word boundaries, line break /// opportunities, etc. class UnicodePropertyLookup
{ UnicodePropertyLookup(this.ranges, this.defaultProperty); /// Creates a [UnicodePropertyLookup] from packed line break data. factory UnicodePropertyLookup.fromPackedData( String packedData, int singleRangesCount, List
propertyEnumValues, P defaultProperty, ) { return UnicodePropertyLookup
( _unpackProperties
(packedData, singleRangesCount, propertyEnumValues),
defaultProperty,
);
}
/// The list of unicode ranges and their associated properties.
final List range = ranges[mid];
switch (range.compare(value)) {
case _ComparisonResult.higher:
min = mid + 1;
case _ComparisonResult.lower:
max = mid;
case _ComparisonResult.inside:
return mid;
}
}
return -1;
}
}
List (
String packedData,
int singleRangesCount,
List propertyEnumValues,
) {
// Packed data is mostly structured in chunks of 9 characters each:
//
// * [0..3]: Range start, encoded as a base36 integer.
// * [4..7]: Range end, encoded as a base36 integer.
// * [8]: Index of the property enum value, encoded as a single letter.
//
// When the range is a single number (i.e. range start == range end), it gets
// packed more efficiently in a chunk of 6 characters:
//
// * [0..3]: Range start (and range end), encoded as a base 36 integer.
// * [4]: "!" to indicate that there's no range end.
// * [5]: Index of the property enum value, encoded as a single letter.
// `packedData.length + singleRangesCount * 3` would have been the size of the
// packed data if the efficient packing of single-range items wasn't applied.
assert((packedData.length + singleRangesCount * 3) % 9 == 0);
final List (rangeStart, rangeEnd, property));
}
return ranges;
}
int _getEnumIndexFromPackedValue(int charCode) {
// This has to stay in sync with [EnumValue.serialized] in
// `tool/unicode_sync_script.dart`.
assert(
(charCode >= kChar_A && charCode <= kChar_Z) || (charCode >= kChar_a && charCode <= kChar_z),
);
// Uppercase letters were assigned to the first 26 enum values.
if (charCode <= kChar_Z) {
return charCode - kChar_A;
}
// Lowercase letters were assigned to enum values above 26.
return 26 + charCode - kChar_a;
}
int _consumeInt(String packedData, int index) {
// The implementation is equivalent to:
//
// ```dart
// return int.tryParse(packedData.substring(index, index + 4), radix: 36);
// ```
//
// But using substring is slow when called too many times. This custom
// implementation makes the unpacking 25%-45% faster than using substring.
final int digit0 = getIntFromCharCode(packedData.codeUnitAt(index + 3));
final int digit1 = getIntFromCharCode(packedData.codeUnitAt(index + 2));
final int digit2 = getIntFromCharCode(packedData.codeUnitAt(index + 1));
final int digit3 = getIntFromCharCode(packedData.codeUnitAt(index));
return digit0 + (digit1 * 36) + (digit2 * 36 * 36) + (digit3 * 36 * 36 * 36);
}
/// Does the same thing as [int.parse(str, 36)] but takes only a single
/// character as a [charCode] integer.
int getIntFromCharCode(int charCode) {
assert(
(charCode >= kChar_0 && charCode <= kChar_9) || (charCode >= kChar_a && charCode <= kChar_z),
);
if (charCode <= kChar_9) {
return charCode - kChar_0;
}
// "a" starts from 10 and remaining letters go up from there.
return charCode - kChar_a + 10;
}