CharacterTypes.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  1. /*
  2. * Copyright (c) 2021, Tim Flynn <trflynn89@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/CharacterTypes.h>
  7. #include <AK/Platform.h>
  8. #include <AK/StringBuilder.h>
  9. #include <AK/Types.h>
  10. #include <AK/Utf8View.h>
  11. #include <LibUnicode/CharacterTypes.h>
  12. #include <LibUnicode/Locale.h>
  13. #if ENABLE_UNICODE_DATA
  14. # include <LibUnicode/UnicodeData.h>
  15. #endif
  16. // For details on the algorithms used here, see Section 3.13 Default Case Algorithms
  17. // https://www.unicode.org/versions/Unicode13.0.0/ch03.pdf
  18. namespace Unicode {
  19. Optional<String> __attribute__((weak)) code_point_display_name(u32) { return {}; }
  20. Optional<StringView> __attribute__((weak)) code_point_abbreviation(u32) { return {}; }
  21. u32 __attribute__((weak)) canonical_combining_class(u32) { return {}; }
  22. Span<SpecialCasing const* const> __attribute__((weak)) special_case_mapping(u32) { return {}; }
  23. #if ENABLE_UNICODE_DATA
  24. static bool is_after_uppercase_i(Utf8View const& string, size_t index)
  25. {
  26. // There is an uppercase I before C, and there is no intervening combining character class 230 (Above) or 0.
  27. auto preceding_view = string.substring_view(0, index);
  28. bool found_uppercase_i = false;
  29. // FIXME: Would be better if Utf8View supported reverse iteration.
  30. for (auto code_point : preceding_view) {
  31. if (code_point == 'I') {
  32. found_uppercase_i = true;
  33. continue;
  34. }
  35. u32 combining_class = canonical_combining_class(code_point);
  36. if (combining_class == 0)
  37. found_uppercase_i = false;
  38. else if (combining_class == 230)
  39. found_uppercase_i = false;
  40. }
  41. return found_uppercase_i;
  42. }
  43. static bool is_after_soft_dotted_code_point(Utf8View const& string, size_t index)
  44. {
  45. // There is a Soft_Dotted character before C, with no intervening character of combining class 0 or 230 (Above).
  46. auto preceding_view = string.substring_view(0, index);
  47. bool found_soft_dotted_code_point = false;
  48. // FIXME: Would be better if Utf8View supported reverse iteration.
  49. for (auto code_point : preceding_view) {
  50. if (code_point_has_property(code_point, Property::Soft_Dotted)) {
  51. found_soft_dotted_code_point = true;
  52. continue;
  53. }
  54. u32 combining_class = canonical_combining_class(code_point);
  55. if (combining_class == 0)
  56. found_soft_dotted_code_point = false;
  57. else if (combining_class == 230)
  58. found_soft_dotted_code_point = false;
  59. }
  60. return found_soft_dotted_code_point;
  61. }
  62. static bool is_final_code_point(Utf8View const& string, size_t index, size_t byte_length)
  63. {
  64. // C is preceded by a sequence consisting of a cased letter and then zero or more case-ignorable
  65. // characters, and C is not followed by a sequence consisting of zero or more case-ignorable
  66. // characters and then a cased letter.
  67. auto preceding_view = string.substring_view(0, index);
  68. auto following_view = ((index + byte_length) < string.byte_length())
  69. ? string.substring_view(index + byte_length)
  70. : Utf8View {};
  71. size_t cased_letter_count = 0;
  72. for (auto code_point : preceding_view) {
  73. bool is_cased = code_point_has_property(code_point, Property::Cased);
  74. bool is_case_ignorable = code_point_has_property(code_point, Property::Case_Ignorable);
  75. if (is_cased && !is_case_ignorable)
  76. ++cased_letter_count;
  77. else if (!is_case_ignorable)
  78. cased_letter_count = 0;
  79. }
  80. if (cased_letter_count == 0)
  81. return false;
  82. for (auto code_point : following_view) {
  83. bool is_cased = code_point_has_property(code_point, Property::Cased);
  84. bool is_case_ignorable = code_point_has_property(code_point, Property::Case_Ignorable);
  85. if (is_case_ignorable)
  86. continue;
  87. if (is_cased)
  88. return false;
  89. break;
  90. }
  91. return true;
  92. }
  93. static bool is_followed_by_combining_class_above(Utf8View const& string, size_t index, size_t byte_length)
  94. {
  95. // C is followed by a character of combining class 230 (Above) with no intervening character of combining class 0 or 230 (Above).
  96. auto following_view = ((index + byte_length) < string.byte_length())
  97. ? string.substring_view(index + byte_length)
  98. : Utf8View {};
  99. for (auto code_point : following_view) {
  100. u32 combining_class = canonical_combining_class(code_point);
  101. if (combining_class == 0)
  102. return false;
  103. if (combining_class == 230)
  104. return true;
  105. }
  106. return false;
  107. }
  108. static bool is_followed_by_combining_dot_above(Utf8View const& string, size_t index, size_t byte_length)
  109. {
  110. // C is followed by combining dot above (U+0307). Any sequence of characters with a combining class that is neither 0 nor 230 may
  111. // intervene between the current character and the combining dot above.
  112. auto following_view = ((index + byte_length) < string.byte_length())
  113. ? string.substring_view(index + byte_length)
  114. : Utf8View {};
  115. for (auto code_point : following_view) {
  116. if (code_point == 0x307)
  117. return true;
  118. u32 combining_class = canonical_combining_class(code_point);
  119. if (combining_class == 0)
  120. return false;
  121. if (combining_class == 230)
  122. return false;
  123. }
  124. return false;
  125. }
  126. static SpecialCasing const* find_matching_special_case(u32 code_point, Utf8View const& string, Optional<StringView> locale, size_t index, size_t byte_length)
  127. {
  128. auto requested_locale = Locale::None;
  129. if (locale.has_value()) {
  130. if (auto maybe_locale = locale_from_string(*locale); maybe_locale.has_value())
  131. requested_locale = *maybe_locale;
  132. }
  133. auto special_casings = special_case_mapping(code_point);
  134. for (auto const* special_casing : special_casings) {
  135. if (special_casing->locale != Locale::None && special_casing->locale != requested_locale)
  136. continue;
  137. switch (special_casing->condition) {
  138. case Condition::None:
  139. return special_casing;
  140. case Condition::AfterI:
  141. if (is_after_uppercase_i(string, index))
  142. return special_casing;
  143. break;
  144. case Condition::AfterSoftDotted:
  145. if (is_after_soft_dotted_code_point(string, index))
  146. return special_casing;
  147. break;
  148. case Condition::FinalSigma:
  149. if (is_final_code_point(string, index, byte_length))
  150. return special_casing;
  151. break;
  152. case Condition::MoreAbove:
  153. if (is_followed_by_combining_class_above(string, index, byte_length))
  154. return special_casing;
  155. break;
  156. case Condition::NotBeforeDot:
  157. if (!is_followed_by_combining_dot_above(string, index, byte_length))
  158. return special_casing;
  159. break;
  160. }
  161. }
  162. return nullptr;
  163. }
  164. #endif
  165. u32 __attribute__((weak)) to_unicode_lowercase(u32 code_point)
  166. {
  167. return to_ascii_lowercase(code_point);
  168. }
  169. u32 __attribute__((weak)) to_unicode_uppercase(u32 code_point)
  170. {
  171. return to_ascii_uppercase(code_point);
  172. }
  173. String to_unicode_lowercase_full(StringView string, [[maybe_unused]] Optional<StringView> locale)
  174. {
  175. #if ENABLE_UNICODE_DATA
  176. Utf8View view { string };
  177. StringBuilder builder;
  178. size_t index = 0;
  179. size_t byte_length = 0;
  180. for (auto it = view.begin(); it != view.end(); ++it, index += byte_length) {
  181. u32 code_point = *it;
  182. byte_length = it.underlying_code_point_length_in_bytes();
  183. auto const* special_casing = find_matching_special_case(code_point, view, locale, index, byte_length);
  184. if (!special_casing) {
  185. builder.append_code_point(to_unicode_lowercase(code_point));
  186. continue;
  187. }
  188. for (size_t i = 0; i < special_casing->lowercase_mapping_size; ++i)
  189. builder.append_code_point(special_casing->lowercase_mapping[i]);
  190. }
  191. return builder.build();
  192. #else
  193. return string.to_lowercase_string();
  194. #endif
  195. }
  196. String to_unicode_uppercase_full(StringView string, [[maybe_unused]] Optional<StringView> locale)
  197. {
  198. #if ENABLE_UNICODE_DATA
  199. Utf8View view { string };
  200. StringBuilder builder;
  201. size_t index = 0;
  202. size_t byte_length = 0;
  203. for (auto it = view.begin(); it != view.end(); ++it, index += byte_length) {
  204. u32 code_point = *it;
  205. byte_length = it.underlying_code_point_length_in_bytes();
  206. auto const* special_casing = find_matching_special_case(code_point, view, locale, index, byte_length);
  207. if (!special_casing) {
  208. builder.append_code_point(to_unicode_uppercase(code_point));
  209. continue;
  210. }
  211. for (size_t i = 0; i < special_casing->uppercase_mapping_size; ++i)
  212. builder.append_code_point(special_casing->uppercase_mapping[i]);
  213. }
  214. return builder.build();
  215. #else
  216. return string.to_uppercase_string();
  217. #endif
  218. }
  219. Optional<GeneralCategory> __attribute__((weak)) general_category_from_string(StringView) { return {}; }
  220. bool __attribute__((weak)) code_point_has_general_category(u32, GeneralCategory) { return {}; }
  221. Optional<Property> __attribute__((weak)) property_from_string(StringView) { return {}; }
  222. bool __attribute__((weak)) code_point_has_property(u32, Property) { return {}; }
  223. bool is_ecma262_property([[maybe_unused]] Property property)
  224. {
  225. #if ENABLE_UNICODE_DATA
  226. // EMCA-262 only allows a subset of Unicode properties: https://tc39.es/ecma262/#table-binary-unicode-properties
  227. switch (property) {
  228. case Unicode::Property::ASCII:
  229. case Unicode::Property::ASCII_Hex_Digit:
  230. case Unicode::Property::Alphabetic:
  231. case Unicode::Property::Any:
  232. case Unicode::Property::Assigned:
  233. case Unicode::Property::Bidi_Control:
  234. case Unicode::Property::Bidi_Mirrored:
  235. case Unicode::Property::Case_Ignorable:
  236. case Unicode::Property::Cased:
  237. case Unicode::Property::Changes_When_Casefolded:
  238. case Unicode::Property::Changes_When_Casemapped:
  239. case Unicode::Property::Changes_When_Lowercased:
  240. case Unicode::Property::Changes_When_NFKC_Casefolded:
  241. case Unicode::Property::Changes_When_Titlecased:
  242. case Unicode::Property::Changes_When_Uppercased:
  243. case Unicode::Property::Dash:
  244. case Unicode::Property::Default_Ignorable_Code_Point:
  245. case Unicode::Property::Deprecated:
  246. case Unicode::Property::Diacritic:
  247. case Unicode::Property::Emoji:
  248. case Unicode::Property::Emoji_Component:
  249. case Unicode::Property::Emoji_Modifier:
  250. case Unicode::Property::Emoji_Modifier_Base:
  251. case Unicode::Property::Emoji_Presentation:
  252. case Unicode::Property::Extended_Pictographic:
  253. case Unicode::Property::Extender:
  254. case Unicode::Property::Grapheme_Base:
  255. case Unicode::Property::Grapheme_Extend:
  256. case Unicode::Property::Hex_Digit:
  257. case Unicode::Property::IDS_Binary_Operator:
  258. case Unicode::Property::IDS_Trinary_Operator:
  259. case Unicode::Property::ID_Continue:
  260. case Unicode::Property::ID_Start:
  261. case Unicode::Property::Ideographic:
  262. case Unicode::Property::Join_Control:
  263. case Unicode::Property::Logical_Order_Exception:
  264. case Unicode::Property::Lowercase:
  265. case Unicode::Property::Math:
  266. case Unicode::Property::Noncharacter_Code_Point:
  267. case Unicode::Property::Pattern_Syntax:
  268. case Unicode::Property::Pattern_White_Space:
  269. case Unicode::Property::Quotation_Mark:
  270. case Unicode::Property::Radical:
  271. case Unicode::Property::Regional_Indicator:
  272. case Unicode::Property::Sentence_Terminal:
  273. case Unicode::Property::Soft_Dotted:
  274. case Unicode::Property::Terminal_Punctuation:
  275. case Unicode::Property::Unified_Ideograph:
  276. case Unicode::Property::Uppercase:
  277. case Unicode::Property::Variation_Selector:
  278. case Unicode::Property::White_Space:
  279. case Unicode::Property::XID_Continue:
  280. case Unicode::Property::XID_Start:
  281. return true;
  282. default:
  283. return false;
  284. }
  285. #else
  286. return false;
  287. #endif
  288. }
  289. Optional<Script> __attribute__((weak)) script_from_string(StringView) { return {}; }
  290. bool __attribute__((weak)) code_point_has_script(u32, Script) { return {}; }
  291. bool __attribute__((weak)) code_point_has_script_extension(u32, Script) { return {}; }
  292. bool __attribute__((weak)) code_point_has_grapheme_break_property(u32, GraphemeBreakProperty) { return {}; }
  293. bool __attribute__((weak)) code_point_has_word_break_property(u32, WordBreakProperty) { return {}; }
  294. bool __attribute__((weak)) code_point_has_sentence_break_property(u32, SentenceBreakProperty) { return {}; }
  295. }