MSTL 1.4.0
A Modern C++ Library with extended functionality, web components, and utility libraries
载入中...
搜索中...
未找到
character.hpp
1#ifndef MSTL_CORE_STRING_CHARACTER_HPP__
2#define MSTL_CORE_STRING_CHARACTER_HPP__
3#include "../interface/icharacter.hpp"
5
7
8template <typename T>
9MSTL_ALWAYS_INLINE constexpr void __append_utf8_char_aux(T&) {}
10template <>
11MSTL_CONSTEXPR20 void __append_utf8_char_aux<string>(string& result) {
12 result.append("\xEF\xBF\xBD", 3);
13}
14#ifdef MSTL_STANDARD_20__
15template <>
16MSTL_CONSTEXPR20 void __append_utf8_char_aux<u8string>(u8string& result) {
17 result.append(u8"\xEF\xBF\xBD", 3);
18}
19#endif
20
21template <typename T>
22MSTL_CONSTEXPR20 void append_utf8_char(basic_string<T>& result, uint32_t cp) {
23 if (cp > 0x10FFFF || _MSTL is_high_surrogate(cp) || _MSTL is_low_surrogate(cp)) {
24 _INNER __append_utf8_char_aux(result);
25 return;
26 }
27
28 if (cp <= 0x7F) {
29 result.push_back(static_cast<T>(cp));
30 } else if (cp <= 0x7FF) {
31 result.push_back(static_cast<T>(0xC0 | (cp >> 6)));
32 result.push_back(static_cast<T>(0x80 | (cp & 0x3F)));
33 } else if (cp <= 0xFFFF) {
34 result.push_back(static_cast<T>(0xE0 | (cp >> 12)));
35 result.push_back(static_cast<T>(0x80 | ((cp >> 6) & 0x3F)));
36 result.push_back(static_cast<T>(0x80 | (cp & 0x3F)));
37 } else {
38 result.push_back(static_cast<T>(0xF0 | (cp >> 18)));
39 result.push_back(static_cast<T>(0x80 | ((cp >> 12) & 0x3F)));
40 result.push_back(static_cast<T>(0x80 | ((cp >> 6) & 0x3F)));
41 result.push_back(static_cast<T>(0x80 | (cp & 0x3F)));
42 }
43}
44
45constexpr bool decode_utf8_char(const byte_t* data, size_t& i, const size_t len, uint32_t& cp) noexcept {
46 if (i >= len) {
47 cp = 0xFFFD;
48 return false;
49 }
50
51 const byte_t b1 = data[i++];
52 if ((b1 & 0x80) == 0) {
53 cp = b1;
54 return true;
55 } else if ((b1 & 0xE0) == 0xC0) {
56 if (i >= len) return false;
57 const byte_t b2 = data[i++];
58 if ((b2 & 0xC0) != 0x80) return false;
59 cp = ((b1 & 0x1F) << 6) | (b2 & 0x3F);
60 return cp >= 0x80;
61 } else if ((b1 & 0xF0) == 0xE0) {
62 if (i + 1 >= len) return false;
63 const byte_t b2 = data[i++];
64 const byte_t b3 = data[i++];
65 if ((b2 & 0xC0) != 0x80 || (b3 & 0xC0) != 0x80) return false;
66 cp = ((b1 & 0x0F) << 12) | ((b2 & 0x3F) << 6) | (b3 & 0x3F);
67 return cp >= 0x800 && !(cp >= 0xD800 && cp <= 0xDFFF);
68 } else if ((b1 & 0xF8) == 0xF0) {
69 if (i + 2 >= len) return false;
70 const byte_t b2 = data[i++];
71 const byte_t b3 = data[i++];
72 const byte_t b4 = data[i++];
73 if ((b2 & 0xC0) != 0x80 || (b3 & 0xC0) != 0x80 || (b4 & 0xC0) != 0x80) return false;
74 cp = ((b1 & 0x07) << 18) | ((b2 & 0x3F) << 12) | ((b3 & 0x3F) << 6) | (b4 & 0x3F);
75 return cp >= 0x10000 && cp <= 0x10FFFF;
76 }
77
78 cp = 0xFFFD;
79 return false;
80}
81
82template <typename T>
83constexpr bool get_utf16_codepoint(const T* obj, size_t i, const size_t len, uint32_t& cp, size_t& chars_consumed) {
84 const auto c1 = static_cast<uint32_t>(obj[i]);
85 chars_consumed = 1;
86
87 if (_MSTL is_high_surrogate(c1)) {
88 if (i + 1 < len) {
89 const auto c2 = static_cast<uint32_t>(obj[i + 1]);
90 if (_MSTL is_low_surrogate(c2)) {
91 cp = _MSTL combine_surrogates(c1, c2);
92 chars_consumed = 2;
93 return true;
94 }
95 }
96 cp = 0xFFFD;
97 return true;
98 } else if (_MSTL is_low_surrogate(c1)) {
99 cp = 0xFFFD;
100 return true;
101 }
102
103 cp = c1;
104 return true;
105}
106
107template <typename T>
108constexpr bool handle_utf16_surrogate_pair(
109 const T* obj, size_t& i, const size_t len, uint32_t& cp) noexcept {
110 const auto c1 = static_cast<uint32_t>(obj[i]);
111 if (_MSTL is_high_surrogate(c1)) {
112 if (i + 1 < len) {
113 const auto c2 = static_cast<uint32_t>(obj[i + 1]);
114 if (_MSTL is_low_surrogate(c2)) {
115 cp = _MSTL combine_surrogates(c1, c2);
116 i += 2;
117 return true;
118 }
119 }
120 cp = 0xFFFD;
121 i += 1;
122 return true;
123 } else if (_MSTL is_low_surrogate(c1)) {
124 cp = 0xFFFD;
125 i += 1;
126 return true;
127 }
128 cp = c1;
129 i += 1;
130 return true;
131}
132
133constexpr bool is_valid_unicode_codepoint(const uint32_t cp) noexcept {
134 return cp <= 0x10FFFF && !_MSTL is_high_surrogate(cp) && !_MSTL is_low_surrogate(cp);
135}
136
137template <typename T>
138MSTL_CONSTEXPR20 void codepoint_to_utf16(basic_string<T>& result, uint32_t cp) {
139 if (!_INNER is_valid_unicode_codepoint(cp)) {
140 result.push_back(0xFFFD);
141 return;
142 }
143
144 if (cp <= 0xFFFF) {
145 if (cp >= 0xD800 && cp <= 0xDFFF) {
146 result.push_back(0xFFFD);
147 } else {
148 result.push_back(static_cast<T>(cp));
149 }
150 } else {
151 const uint32_t adjusted = cp - 0x10000;
152 const auto high_surrogate = static_cast<T>((adjusted >> 10) + 0xD800);
153 const auto low_surrogate = static_cast<T>((adjusted & 0x3FF) + 0xDC00);
154 result.push_back(high_surrogate);
155 result.push_back(low_surrogate);
156 }
157}
158
159template <typename T>
160MSTL_CONSTEXPR20 void codepoint_to_wchar(basic_string<T>& result, uint32_t cp) {
161 if (!_INNER is_valid_unicode_codepoint(cp)) {
162 result.push_back(0xFFFD);
163 return;
164 }
165
166#ifdef MSTL_PLATFORM_WINDOWS__
167 if (cp <= 0xFFFF) {
168 result.push_back(static_cast<wchar_t>(cp));
169 } else {
170 const uint32_t adjusted = cp - 0x10000;
171 const wchar_t high_surrogate = static_cast<wchar_t>((adjusted >> 10) + 0xD800);
172 const wchar_t low_surrogate = static_cast<wchar_t>((adjusted & 0x3FF) + 0xDC00);
173 result.push_back(high_surrogate);
174 result.push_back(low_surrogate);
175 }
176#elif defined(MSTL_PLATFORM_LINUX__)
177 result.push_back(static_cast<wchar_t>(cp));
178#endif
179}
180
181template <typename T, typename U>
182MSTL_CONSTEXPR20 void append_ascii_chars(basic_string<T>& result, const U* str, size_t len) {
183 result.reserve(result.size() + len);
184 for (size_t i = 0; i < len; ++i) {
185 result.push_back(static_cast<T>(static_cast<byte_t>(str[i])));
186 }
187}
188
190
191#define MSTL_BUILD_PACKAGE_CONSTRUCTOR(T) \
192constexpr T() noexcept = default; \
193constexpr T(const T&) noexcept = default; \
194constexpr T(T&&) noexcept = default; \
195constexpr T(value_type val) noexcept : base(val) {} \
196MSTL_CONSTEXPR20 ~T() = default; \
197constexpr T& operator =(const T& other) noexcept { \
198 value_ = other.value_; \
199 return *this; \
200} \
201constexpr T& operator =(T&& other) noexcept { \
202 value_ = other.value_; \
203 other.value_ = initialize<package_type>(); \
204 return *this; \
205} \
206constexpr T& operator =(value_type other) noexcept { \
207 value_ = other; \
208 return *this; \
209}
210
211
212struct character : icharacter<character, char> {
213 using value_type = char;
214 using base = icharacter<character, char>;
215
216 MSTL_BUILD_PACKAGE_CONSTRUCTOR(character)
217
218 static MSTL_CONSTEXPR20 string to_string(const basic_string_view<value_type>& obj) {
219 return string{obj};
220 }
221
222 static MSTL_CONSTEXPR20 wstring to_wstring(const basic_string_view<value_type>& obj) {
223 if (obj.empty()) return {};
224 wstring result;
225
226 const auto* data = reinterpret_cast<const byte_t*>(obj.data());
227 size_t i = 0;
228 const size_t len = obj.size();
229 result.reserve(len);
230
231 while (i < len) {
232 uint32_t cp;
233 if (_INNER decode_utf8_char(data, i, len, cp)) {
234 _INNER codepoint_to_wchar(result, cp);
235 } else {
236 _INNER codepoint_to_wchar(result, 0xFFFD);
237 }
238 }
239 return result;
240 }
241
242#ifdef MSTL_STANDARD_20__
243 static MSTL_CONSTEXPR20 u8string to_u8string(const basic_string_view<value_type>& obj) {
244 if (obj.empty()) return {};
245 u8string result;
246 result.reserve(obj.size());
247 for (const char c : obj) {
248 result.push_back(static_cast<char8_t>(static_cast<byte_t>(c)));
249 }
250 return result;
251 }
252#endif
253
254 static MSTL_CONSTEXPR20 u16string to_u16string(const basic_string_view<value_type>& obj) {
255 if (obj.empty()) return {};
256 u16string result;
257
258 const auto* data = reinterpret_cast<const byte_t*>(obj.data());
259 size_t i = 0;
260 const size_t len = obj.size();
261 result.reserve(len * 2);
262
263 while (i < len) {
264 uint32_t cp;
265 if (_INNER decode_utf8_char(data, i, len, cp)) {
266 _INNER codepoint_to_utf16(result, cp);
267 } else {
268 result.push_back(0xFFFD);
269 }
270 }
271 return result;
272 }
273
274 static MSTL_CONSTEXPR20 u32string to_u32string(const basic_string_view<value_type>& obj) {
275 if (obj.empty()) return {};
276 u32string result;
277
278 const auto* data = reinterpret_cast<const byte_t*>(obj.data());
279 size_t i = 0;
280 const size_t len = obj.size();
281 result.reserve(len);
282
283 while (i < len) {
284 uint32_t cp;
285 if (_INNER decode_utf8_char(data, i, len, cp)) {
286 result.push_back(static_cast<char32_t>(cp));
287 } else {
288 result.push_back(0xFFFD);
289 }
290 }
291 return result;
292 }
293};
294
295template <>
296struct package<char> {
297 using type = character;
298};
299template <>
300struct unpackage<character> {
301 using type = char;
302};
303
304
305struct wcharacter : icharacter<wcharacter, wchar_t> {
306 using value_type = wchar_t;
307 using base = icharacter<wcharacter, wchar_t>;
308
309 MSTL_BUILD_PACKAGE_CONSTRUCTOR(wcharacter)
310
311 static MSTL_CONSTEXPR20 string to_string(const basic_string_view<value_type>& obj) {
312 if (obj.empty()) return {};
313 string result;
314
315#ifdef MSTL_PLATFORM_WINDOWS__
316 for (size_t i = 0; i < obj.size(); ) {
317 uint32_t cp;
318 size_t chars_consumed;
319 if (_INNER get_utf16_codepoint(obj.data(), i, obj.size(), cp, chars_consumed)) {
320 _INNER append_utf8_char(result, cp);
321 i += chars_consumed;
322 } else {
323 _INNER append_utf8_char(result, 0xFFFD);
324 i++;
325 }
326 }
327#elif defined(MSTL_PLATFORM_LINUX__)
328 for (const value_type i : obj) {
329 _INNER append_utf8_char(result, i);
330 }
331#endif
332 return result;
333 }
334
335 static MSTL_CONSTEXPR20 wstring to_wstring(const basic_string_view<value_type>& obj) {
336 return wstring{obj};
337 }
338
339#ifdef MSTL_STANDARD_20__
340 static MSTL_CONSTEXPR20 u8string to_u8string(const basic_string_view<value_type>& obj) {
341 if (obj.empty()) return {};
342 u8string result;
343
344#ifdef MSTL_PLATFORM_WINDOWS__
345 for (size_t i = 0; i < obj.size(); ) {
346 uint32_t cp;
347 size_t chars_consumed;
348 if (_INNER get_utf16_codepoint(obj.data(), i, obj.size(), cp, chars_consumed)) {
349 _INNER append_utf8_char(result, cp);
350 i += chars_consumed;
351 } else {
352 _INNER append_utf8_char(result, 0xFFFD);
353 i++;
354 }
355 }
356#elif defined(MSTL_PLATFORM_LINUX__)
357 for (const value_type i : obj) {
358 _INNER append_utf8_char(result, i);
359 }
360#endif
361 return result;
362 }
363#endif
364
365 static MSTL_CONSTEXPR20 u16string to_u16string(const basic_string_view<value_type>& obj) {
366 if (obj.empty()) return {};
367 u16string result;
368
369#ifdef MSTL_PLATFORM_WINDOWS__
370 result.reserve(obj.size());
371 for (size_t i = 0; i < obj.size(); ++i) {
372 result.push_back(static_cast<char16_t>(static_cast<uint16_t>(obj[i])));
373 }
374#elif defined(MSTL_PLATFORM_LINUX__)
375 result.reserve(obj.size() * 2);
376 for (const value_type i : obj) {
377 _INNER codepoint_to_utf16(result, i);
378 }
379#endif
380 return result;
381 }
382
383 static MSTL_CONSTEXPR20 u32string to_u32string(const basic_string_view<value_type>& obj) {
384 if (obj.empty()) return {};
385 u32string result;
386 result.reserve(obj.size());
387
388#ifdef MSTL_PLATFORM_WINDOWS__
389 for (size_t i = 0; i < obj.size(); ) {
390 uint32_t cp;
391 size_t chars_consumed;
392 if (_INNER get_utf16_codepoint(obj.data(), i, obj.size(), cp, chars_consumed)) {
393 result.push_back(static_cast<char32_t>(cp));
394 i += chars_consumed;
395 } else {
396 result.push_back(0xFFFD);
397 i++;
398 }
399 }
400#elif defined(MSTL_PLATFORM_LINUX__)
401 for (const value_type i : obj) {
402 result.push_back(static_cast<char32_t>(i));
403 }
404#endif
405 return result;
406 }
407};
408
409template <>
410struct package<wchar_t> {
411 using type = wcharacter;
412};
413template <>
414struct unpackage<wcharacter> {
415 using type = wchar_t;
416};
417
418
419#ifdef MSTL_STANDARD_20__
420
421struct u8character : icharacter<u8character, char8_t> {
422 using value_type = char8_t;
423 using base = icharacter<u8character, char8_t>;
424
425 MSTL_BUILD_PACKAGE_CONSTRUCTOR(u8character)
426
427 static MSTL_CONSTEXPR20 string to_string(const basic_string_view<value_type>& obj) {
428 if (obj.empty()) return {};
429 string result;
430 _INNER append_ascii_chars(result, obj.data(), obj.size());
431 return result;
432 }
433
434 static MSTL_CONSTEXPR20 wstring to_wstring(const basic_string_view<value_type>& obj) {
435 if (obj.empty()) return {};
436 wstring result;
437 const size_t len = obj.size();
438 result.reserve(len);
439
440 size_t i = 0;
441 while (i < len) {
442 const auto data = reinterpret_cast<const byte_t*>(obj.data());
443 uint32_t cp;
444 if (_INNER decode_utf8_char(data, i, len, cp)) {
445 _INNER codepoint_to_wchar(result, cp);
446 } else {
447 result.push_back(0xFFFD);
448 }
449 }
450 return result;
451 }
452
453 static MSTL_CONSTEXPR20 u8string to_u8string(const basic_string_view<value_type>& obj) {
454 return u8string{obj};
455 }
456
457 static MSTL_CONSTEXPR20 u16string to_u16string(const basic_string_view<value_type>& obj) {
458 if (obj.empty()) return {};
459 u16string result;
460 const size_t len = obj.size();
461 result.reserve(len);
462
463 size_t i = 0;
464 while (i < len) {
465 const auto data = reinterpret_cast<const byte_t*>(obj.data());
466 uint32_t cp;
467 if (_INNER decode_utf8_char(data, i, len, cp)) {
468 _INNER codepoint_to_utf16(result, cp);
469 } else {
470 result.push_back(0xFFFD);
471 }
472 }
473 return result;
474 }
475
476 static MSTL_CONSTEXPR20 u32string to_u32string(const basic_string_view<value_type>& obj) {
477 if (obj.empty()) return {};
478 u32string result;
479 const size_t len = obj.size();
480 result.reserve(len);
481
482 size_t i = 0;
483 while (i < len) {
484 const auto data = reinterpret_cast<const byte_t*>(obj.data());
485 uint32_t cp;
486 if (_INNER decode_utf8_char(data, i, len, cp)) {
487 result.push_back(static_cast<char32_t>(cp));
488 } else {
489 result.push_back(0xFFFD);
490 }
491 }
492 return result;
493 }
494};
495
496template <>
497struct package<char8_t> {
498 using type = u8character;
499};
500template <>
501struct unpackage<u8character> {
502 using type = char8_t;
503};
504
505#endif
506
507
508struct u16character : icharacter<u16character, char16_t> {
509 using value_type = char16_t;
510 using base = icharacter<u16character, char16_t>;
511
512 MSTL_BUILD_PACKAGE_CONSTRUCTOR(u16character)
513
514 static MSTL_CONSTEXPR20 string to_string(const basic_string_view<value_type>& obj) {
515 if (obj.empty()) return {};
516 string result;
517
518 size_t start_pos = 0;
519 if (!obj.empty() && obj[0] == 0xFEFF) {
520 start_pos = 1;
521 }
522
523 for (size_t i = start_pos; i < obj.size(); ) {
524 uint32_t cp;
525 size_t chars_consumed;
526
527 if (_INNER get_utf16_codepoint(obj.data(), i, obj.size(), cp, chars_consumed)) {
528 if (cp <= 0x10FFFF && !_MSTL is_high_surrogate(cp) && !_MSTL is_low_surrogate(cp)) {
529 _INNER append_utf8_char(result, cp);
530 } else {
531 _INNER append_utf8_char(result, 0xFFFD);
532 }
533 i += chars_consumed;
534 } else {
535 _INNER append_utf8_char(result, 0xFFFD);
536 i++;
537 }
538 }
539 return result;
540 }
541
542 static MSTL_CONSTEXPR20 wstring to_wstring(const basic_string_view<value_type>& obj) {
543 if (obj.empty()) return {};
544 wstring result;
545 result.reserve(obj.size());
546
547 for (size_t i = 0; i < obj.size(); ) {
548 if (i == 0 && obj[i] == 0xFEFF) {
549 i++;
550 continue;
551 }
552
553 uint32_t cp;
554 size_t chars_consumed;
555 if (_INNER get_utf16_codepoint(obj.data(), i, obj.size(), cp, chars_consumed)) {
556 _INNER codepoint_to_wchar(result, cp);
557 i += chars_consumed;
558 } else {
559 result.push_back(0xFFFD);
560 i++;
561 }
562 }
563 return result;
564 }
565
566#ifdef MSTL_STANDARD_20__
567 static MSTL_CONSTEXPR20 u8string to_u8string(const basic_string_view<value_type>& obj) {
568 if (obj.empty()) return {};
569 u8string result;
570 result.reserve(obj.size() * 3);
571
572 for (size_t i = 0; i < obj.size(); ) {
573 if (i == 0 && obj[i] == 0xFEFF) {
574 i++;
575 continue;
576 }
577
578 uint32_t cp;
579 size_t chars_consumed;
580 if (_INNER get_utf16_codepoint(obj.data(), i, obj.size(), cp, chars_consumed)) {
581 _INNER append_utf8_char(result, cp);
582 i += chars_consumed;
583 } else {
584 _INNER append_utf8_char(result, 0xFFFD);
585 i++;
586 }
587 }
588 return result;
589 }
590#endif
591
592 static MSTL_CONSTEXPR20 u16string to_u16string(const basic_string_view<value_type>& obj) {
593 return u16string{obj};
594 }
595
596 static MSTL_CONSTEXPR20 u32string to_u32string(const basic_string_view<value_type>& obj) {
597 if (obj.empty()) return {};
598 u32string result;
599 result.reserve(obj.size());
600
601 for (size_t i = 0; i < obj.size(); ) {
602 if (i == 0 && obj[i] == 0xFEFF) {
603 i++;
604 continue;
605 }
606
607 uint32_t cp;
608 size_t chars_consumed;
609 if (_INNER get_utf16_codepoint(obj.data(), i, obj.size(), cp, chars_consumed)) {
610 result.push_back(static_cast<char32_t>(cp));
611 i += chars_consumed;
612 } else {
613 result.push_back(0xFFFD);
614 i++;
615 }
616 }
617 return result;
618 }
619};
620
621template <>
622struct package<char16_t> {
623 using type = u16character;
624};
625template <>
626struct unpackage<u16character> {
627 using type = char16_t;
628};
629
630
631struct u32character : icharacter<u32character, char32_t> {
632 using value_type = char32_t;
633 using base = icharacter<u32character, char32_t>;
634
635 MSTL_BUILD_PACKAGE_CONSTRUCTOR(u32character)
636
637 static MSTL_CONSTEXPR20 string to_string(const basic_string_view<value_type>& obj) {
638 if (obj.empty()) return {};
639 string result;
640 for (const value_type i : obj) {
641 _INNER append_utf8_char(result, i);
642 }
643 return result;
644 }
645
646 static MSTL_CONSTEXPR20 wstring to_wstring(const basic_string_view<value_type>& obj) {
647 if (obj.empty()) return {};
648 wstring result;
649 result.reserve(obj.size());
650 for (const value_type i : obj) {
651 _INNER codepoint_to_wchar(result, i);
652 }
653 return result;
654 }
655
656#ifdef MSTL_STANDARD_20__
657 static MSTL_CONSTEXPR20 u8string to_u8string(const basic_string_view<value_type>& obj) {
658 if (obj.empty()) return {};
659 u8string result;
660 result.reserve(obj.size() * 4);
661 for (const value_type i : obj) {
662 _INNER append_utf8_char(result, i);
663 }
664 return result;
665 }
666#endif
667
668 static MSTL_CONSTEXPR20 u16string to_u16string(const basic_string_view<value_type>& obj) {
669 if (obj.empty()) return {};
670 u16string result;
671 result.reserve(obj.size() * 2);
672 for (const value_type i : obj) {
673 _INNER codepoint_to_utf16(result, i);
674 }
675 return result;
676 }
677
678 static MSTL_CONSTEXPR20 u32string to_u32string(const basic_string_view<value_type>& obj) {
679 return u32string{obj};
680 }
681};
682
683template <>
684struct package<char32_t> {
685 using type = u32character;
686};
687template <>
688struct unpackage<u32character> {
689 using type = char32_t;
690};
691
693#endif // MSTL_CORE_STRING_CHARACTER_HPP__
unsigned char byte_t
字节类型,定义为无符号字符
unsigned int uint32_t
32位无符号整数类型
unsigned short uint16_t
16位无符号整数类型
#define _MSTL
全局命名空间MSTL前缀
#define MSTL_END_INNER__
结束inner命名空间
#define _INNER
inner命名空间前缀
#define MSTL_END_NAMESPACE__
结束全局命名空间MSTL
#define MSTL_BEGIN_NAMESPACE__
开始全局命名空间MSTL
#define MSTL_BEGIN_INNER__
开始inner命名空间
MSTL_NODISCARD MSTL_ALWAYS_INLINE constexpr decltype(auto) data(Container &cont) noexcept(noexcept(cont.data()))
获取容器的底层数据指针
MSTL_CONST_FUNCTION constexpr bool is_high_surrogate(const char16_t c) noexcept
检查字符是否为高代理项
MSTL_CONST_FUNCTION constexpr bool is_low_surrogate(const char16_t c) noexcept
检查字符是否为低代理项
MSTL_CONST_FUNCTION constexpr uint32_t combine_surrogates(const char16_t high, const char16_t low) noexcept
组合高代理项和低代理项为完整的Unicode码点
类型包装器模板
类型解包器模板