header_utils
Loading...
Searching...
No Matches
unicode.h
1
4
5#pragma once
6
7#include "string_ops.h"
8#include "bytes.h"
9#include "ranges.h"
10#include "cpp23.h"
11
13{
19
21 [[nodiscard]] constexpr bool is_high_surrogate(char32_t cp) noexcept;
22
24 [[nodiscard]] constexpr bool is_low_surrogate(char32_t cp) noexcept;
25
27 [[nodiscard]] constexpr bool is_surrogate(char32_t cp) noexcept;
28
30 [[nodiscard]] constexpr bool is_unicode(char32_t cp) noexcept;
31
34 [[nodiscard]] constexpr bool is_unicode_character(char32_t cp) noexcept;
35
37 [[nodiscard]] constexpr char32_t surrogate_pair_to_codepoint(char32_t high, char32_t low) noexcept;
38
39 enum class unicode_plane;
40 [[nodiscard]] constexpr auto get_unicode_plane(char32_t cp) noexcept -> unicode_plane;
41
44 {
45 unknown, utf8, utf16, utf32,
46 utf7,
52 };
53
56 {
58 std::endian endianness;
59
60 [[nodiscard]] constexpr auto operator<=>(text_encoding const& other) const noexcept = default;
61 [[nodiscard]] constexpr operator text_encoding_type() const noexcept { return type; }
62 };
63
67 constexpr inline text_encoding utf8_encoding = { text_encoding_type::utf8, std::endian::native };
68 constexpr inline text_encoding utf16_le_encoding = { text_encoding_type::utf16, std::endian::little };
69 constexpr inline text_encoding utf16_be_encoding = { text_encoding_type::utf16, std::endian::big };
70 constexpr inline text_encoding utf32_le_encoding = { text_encoding_type::utf32, std::endian::little };
71 constexpr inline text_encoding utf32_be_encoding = { text_encoding_type::utf32, std::endian::big };
73 constexpr inline text_encoding unknown_text_encoding = { text_encoding_type::unknown, std::endian::native };
75
77
78 struct text_decode_result;
79
81 [[nodiscard]] text_decode_result decode_codepoint(bytelike_range auto range, text_encoding encoding);
82
85 template <bytelike BYTE_TYPE, size_t N>
86 text_encoding consume_bom(std::span<BYTE_TYPE, N>& spn);
87
90 text_encoding consume_bom(string_view8 auto& sv);
91
94 text_encoding consume_bom(string_view16 auto& sv);
95
98 text_encoding consume_bom(string_view32 auto& sv);
99
102 template <bytelike_range T>
103 [[nodiscard]] text_encoding detect_encoding(T const& range);
104
107
109 [[nodiscard]] constexpr size_t codepoint_utf8_count(char32_t cp) noexcept;
110
113 constexpr char32_t consume_utf8(string_view8 auto& str);
114
117 constexpr char32_t consume_utf8_at_end(string_view8 auto& str);
118
121 constexpr char32_t peek_utf8(string_view8 auto str);
122
125 constexpr char32_t peek_utf8_at_end(string_view8 auto str);
126
129 [[nodiscard]] constexpr size_t codepoint_utf8_count_from_end(stringable8 auto str);
130
133 [[nodiscard]] constexpr size_t count_utf8_codepoints(stringable8 auto str);
134
137 [[nodiscard]] constexpr size_t first_utf8_codepoint_size(stringable8 auto str);
138
142 constexpr char32_t pop_utf8(string8 auto& str);
143
145 constexpr size_t append_utf8(string8 auto& buffer, char32_t cp);
146
150 template <string8 RESULT = std::string>
151 [[nodiscard]] constexpr RESULT to_utf8(char32_t cp);
152
156 template <string8 RESULT = std::string, stringable16 STR>
157 [[nodiscard]] constexpr RESULT to_utf8(STR&& str);
158
162 template <string8 RESULT = std::string, stringable32 STR>
163 [[nodiscard]] constexpr RESULT to_utf8(STR&& str);
164
166 [[nodiscard]] std::string to_string(std::wstring_view str);
167
171 constexpr void transcode_codepage_to_utf8(string8 auto& dest, stringable8 auto source, std::span<char32_t const, 128> codepage_map);
172
178 template <string8 RESULT = std::string>
179 [[nodiscard]] constexpr auto transcode_codepage_to_utf8(stringable8 auto source, std::span<char32_t const, 128> codepage_map) -> RESULT;
180
182
184 template <std::ranges::view R>
185 struct utf8_view;
186
189
192 constexpr char32_t consume_utf16(string_view16 auto& str);
193
196 constexpr size_t append_utf16(string16 auto& buffer, char32_t cp);
197
201 template <string16 RESULT = std::wstring>
202 [[nodiscard]] constexpr RESULT to_utf16(char32_t cp);
203
207 template <string16 RESULT = std::wstring, stringable8 STR>
208 [[nodiscard]] constexpr RESULT to_utf16(STR str);
209
212 [[nodiscard]] std::wstring to_wstring(std::string_view str);
213
215
216
219
222 constexpr char32_t consume_utf32(string_view32 auto& str);
223
226 constexpr size_t append_utf32(string32 auto& buffer, char32_t cp);
227
229
231 template <typename T>
232 constexpr char32_t consume_codepoint(T& str)
233 {
234 if (std::empty(str))
235 return 0;
236 if constexpr (stringable8<T>)
237 return consume_utf8(str);
238 else if constexpr (stringable16<T>)
239 return consume_utf16(str);
240 else if constexpr (stringable32<T>)
241 return consume_utf32(str);
242 else
243 static_assert(stringable8<T>, "Unsupported character type");
244 }
245
247 template <typename T>
248 constexpr void append_codepoint(T& str, char32_t cp)
249 {
250 if constexpr (string8<T>)
251 append_utf8(str, cp);
252 else if constexpr (string16<T>)
253 append_utf16(str, cp);
254 else if constexpr (string32<T>)
255 append_utf32(str, cp);
256 else
257 static_assert(string8<T>, "Unsupported character type");
258 }
259
261 template <typename TO, typename FROM>
262 constexpr void transcode_unicode(FROM const& from, TO& out)
263 {
264 auto from_sv = make_sv(from);
265 while (!std::empty(from_sv))
266 append_codepoint(out, consume_codepoint(from_sv));
267 }
268
270 template <typename TO, typename FROM>
271 [[nodiscard]] constexpr TO transcode_unicode(FROM const& from)
272 {
273 TO result{};
274 transcode_unicode(from, result);
275 return result;
276 }
277
281 template <typename T>
282 constexpr void transcode_codepage_to_unicode(T& dest, stringable8 auto source, std::span<char32_t const, 128> codepage_map);
283
288 template <typename RESULT = std::string>
289 [[nodiscard]] constexpr auto transcode_codepage_to_unicode(stringable8 auto source, std::span<char32_t const, 128> codepage_map) -> RESULT;
290
292 template <typename FUNC>
293 void encode_flag(std::string_view ascii_country_code, FUNC&& encoding_func)
294 {
295 static constexpr char32_t flag_introducer_tag = U'\U0001F3F4';
296 static constexpr char32_t flag_end_tag = U'\U000E007F';
297
298 encoding_func(flag_introducer_tag);
299 for (auto& cp : ascii_country_code)
300 {
301 const char32_t encoded = (uint8_t(cp) & 0x7F) + 0xE0000;
302 encoding_func(encoded);
303 }
304 encoding_func(flag_end_tag);
305 }
306
307 template <typename RESULT = std::string, typename FUNC>
308 [[nodiscard]] constexpr auto encode_flag(std::string_view ascii_country_code) -> RESULT
309 {
310 RESULT result{};
311 encode_flag(ascii_country_code, [&](char32_t cp) { append_codepoint(result, cp); });
312 return result;
313 }
314
315
317
326
327 template <bytelike BYTE_TYPE, size_t N>
328 inline text_encoding consume_bom(std::span<BYTE_TYPE, N>& sv)
329 {
330 static_assert(N == std::dynamic_extent, "consume_bom() can only be used with dynamic spans");
331
332 static constexpr auto bom_for_gb18030 = std::array{ static_cast<BYTE_TYPE>(0x84), static_cast<BYTE_TYPE>(0x31), static_cast<BYTE_TYPE>(0x95), static_cast<BYTE_TYPE>(0x33) };
333 static constexpr auto bom_for_utf32_be = std::array{ static_cast<BYTE_TYPE>(0x00), static_cast<BYTE_TYPE>(0x00), static_cast<BYTE_TYPE>(0xFE), static_cast<BYTE_TYPE>(0xFF) };
334 static constexpr auto bom_for_utf32_le = std::array{ static_cast<BYTE_TYPE>(0xFF), static_cast<BYTE_TYPE>(0xFE), static_cast<BYTE_TYPE>(0x00), static_cast<BYTE_TYPE>(0x00) };
335 static constexpr auto bom_for_utf_ebcdic = std::array{ static_cast<BYTE_TYPE>(0xDD), static_cast<BYTE_TYPE>(0x73), static_cast<BYTE_TYPE>(0x66), static_cast<BYTE_TYPE>(0x73) };
336 static constexpr auto bom_for_utf8 = std::array{ static_cast<BYTE_TYPE>(0xEF), static_cast<BYTE_TYPE>(0xBB), static_cast<BYTE_TYPE>(0xBF) };
337 static constexpr auto bom_for_utf7 = std::array{ static_cast<BYTE_TYPE>(0x2B), static_cast<BYTE_TYPE>(0x2F), static_cast<BYTE_TYPE>(0x76) };
338 static constexpr auto bom_for_utf1 = std::array{ static_cast<BYTE_TYPE>(0xF7), static_cast<BYTE_TYPE>(0x64), static_cast<BYTE_TYPE>(0x4C) };
339 static constexpr auto bom_for_scsu = std::array{ static_cast<BYTE_TYPE>(0x0E), static_cast<BYTE_TYPE>(0xFE), static_cast<BYTE_TYPE>(0xFF) };
340 static constexpr auto bom_for_bocu1 = std::array{ static_cast<BYTE_TYPE>(0xFB), static_cast<BYTE_TYPE>(0xEE), static_cast<BYTE_TYPE>(0x28) };
341 static constexpr auto bom_for_utf16_be = std::array{ static_cast<BYTE_TYPE>(0xFE), static_cast<BYTE_TYPE>(0xFF) };
342 static constexpr auto bom_for_utf16_le = std::array{ static_cast<BYTE_TYPE>(0xFF), static_cast<BYTE_TYPE>(0xFE) };
343
344 if (!sv.empty())
345 {
346 using enum std::endian;
348 if (starts_with(sv, std::span<BYTE_TYPE const>{ bom_for_gb18030 })) { sv = sv.subspan(bom_for_gb18030.size()); return { gb18030, native }; }
349 if (starts_with(sv, std::span<BYTE_TYPE const>{ bom_for_utf32_be })) { sv = sv.subspan(bom_for_utf32_be.size()); return { utf32, big }; }
350 if (starts_with(sv, std::span<BYTE_TYPE const>{ bom_for_utf32_le })) { sv = sv.subspan(bom_for_utf32_le.size()); return { utf32, little }; }
351 if (starts_with(sv, std::span<BYTE_TYPE const>{ bom_for_utf_ebcdic })) { sv = sv.subspan(bom_for_utf_ebcdic.size()); return { utf_ebcdic, native }; }
352 if (starts_with(sv, std::span<BYTE_TYPE const>{ bom_for_utf8 })) { sv = sv.subspan(bom_for_utf8.size()); return { utf8, native }; }
353 if (starts_with(sv, std::span<BYTE_TYPE const>{ bom_for_utf7 })) { sv = sv.subspan(bom_for_utf7.size()); return { utf7, native }; }
354 if (starts_with(sv, std::span<BYTE_TYPE const>{ bom_for_utf1 })) { sv = sv.subspan(bom_for_utf1.size()); return { utf1, native }; }
355 if (starts_with(sv, std::span<BYTE_TYPE const>{ bom_for_scsu })) { sv = sv.subspan(bom_for_scsu.size()); return { scsu, native }; }
356 if (starts_with(sv, std::span<BYTE_TYPE const>{ bom_for_bocu1 })) { sv = sv.subspan(bom_for_bocu1.size()); return { bocu1, native }; }
357 if (starts_with(sv, std::span<BYTE_TYPE const>{ bom_for_utf16_be })) { sv = sv.subspan(bom_for_utf16_be.size()); return { utf16, big }; }
358 if (starts_with(sv, std::span<BYTE_TYPE const>{ bom_for_utf16_le })) { sv = sv.subspan(bom_for_utf16_le.size()); return { utf16, little }; }
359 }
361 }
362
364 {
365 static constexpr std::string_view bom_for_gb18030{ "\x84\x31\x95\x33", 4 };
366 static constexpr std::string_view bom_for_utf32_be{ "\x00\x00\xFE\xFF", 4 };
367 static constexpr std::string_view bom_for_utf32_le{ "\xFF\xFE\x00\x00", 4 };
368 static constexpr std::string_view bom_for_utf_ebcdic{ "\xDD\x73\x66\x73", 4 };
369 static constexpr std::string_view bom_for_utf8{ "\xEF\xBB\xBF", 3 };
370 static constexpr std::string_view bom_for_utf7{ "\x2B\x2F\x76", 3 };
371 static constexpr std::string_view bom_for_utf1{ "\xF7\x64\x4C", 3 };
372 static constexpr std::string_view bom_for_scsu{ "\x0E\xFE\xFF", 3 };
373 static constexpr std::string_view bom_for_bocu1{ "\xFB\xEE\x28", 3 };
374 static constexpr std::string_view bom_for_utf16_be{ "\xFE\xFF", 2 };
375 static constexpr std::string_view bom_for_utf16_le{ "\xFF\xFE", 2 };
376
377 if (!sv.empty())
378 {
379 using enum std::endian;
381 if (sv.starts_with(bom_for_gb18030)) { sv.remove_prefix(bom_for_gb18030.size()); return { gb18030, native }; }
382 if (sv.starts_with(bom_for_utf32_be)) { sv.remove_prefix(bom_for_utf32_be.size()); return { utf32, big }; }
383 if (sv.starts_with(bom_for_utf32_le)) { sv.remove_prefix(bom_for_utf32_le.size()); return { utf32, little }; }
384 if (sv.starts_with(bom_for_utf_ebcdic)) { sv.remove_prefix(bom_for_utf_ebcdic.size()); return { utf_ebcdic, native }; }
385 if (sv.starts_with(bom_for_utf8)) { sv.remove_prefix(bom_for_utf8.size()); return { utf8, native }; }
386 if (sv.starts_with(bom_for_utf7)) { sv.remove_prefix(bom_for_utf7.size()); return { utf7, native }; }
387 if (sv.starts_with(bom_for_utf1)) { sv.remove_prefix(bom_for_utf1.size()); return { utf1, native }; }
388 if (sv.starts_with(bom_for_scsu)) { sv.remove_prefix(bom_for_scsu.size()); return { scsu, native }; }
389 if (sv.starts_with(bom_for_bocu1)) { sv.remove_prefix(bom_for_bocu1.size()); return { bocu1, native }; }
390 if (sv.starts_with(bom_for_utf16_be)) { sv.remove_prefix(bom_for_utf16_be.size()); return { utf16, big }; }
391 if (sv.starts_with(bom_for_utf16_le)) { sv.remove_prefix(bom_for_utf16_le.size()); return { utf16, little }; }
392 }
394 }
395
397 {
398 static constexpr std::string_view bom_for_utf16_be{ "\xFE\xFF", 2 };
399 static constexpr std::string_view bom_for_utf16_le{ "\xFF\xFE", 2 };
400
401 if (!sv.empty())
402 {
403 const auto as_bytes = std::string_view{ (char*)sv.data(), sv.size() * sizeof(char16_t) };
404
405 if (as_bytes.starts_with(bom_for_utf16_be)) { sv.remove_prefix(1); return { text_encoding_type::utf16, std::endian::big }; }
406 if (as_bytes.starts_with(bom_for_utf16_le)) { sv.remove_prefix(1); return { text_encoding_type::utf16, std::endian::little }; }
407 }
409 }
410
412 {
413 static constexpr std::string_view bom_for_utf32_be{ "\xFF\xFE\x00\x00", 4 };
414 static constexpr std::string_view bom_for_utf32_le{ "\x00\x00\xFE\xFF", 4 };
415
416 if (!sv.empty())
417 {
418 const auto as_bytes = std::string_view{ (char*)sv.data(), sv.size() * sizeof(char32_t) };
419
420 if (as_bytes.starts_with(bom_for_utf32_be)) { sv.remove_prefix(1); return { text_encoding_type::utf32, std::endian::big }; }
421 if (as_bytes.starts_with(bom_for_utf32_le)) { sv.remove_prefix(1); return { text_encoding_type::utf32, std::endian::little }; }
422 }
424 }
425
428 {
429 enum status : uint8_t {
430 unsupported_encoding,
431 truncated,
432 invalid,
433 valid,
434 };
435
437 status status = unsupported_encoding;
439 char32_t point = static_cast<char32_t>(-1);
441 uint8_t byte_count = 0;
442 };
443
447 enum class unicode_plane
448 {
451 basic_multilingual_plane,
452 supplementary_multilingual_plane,
453 supplementary_ideographic_plane,
454 tertiary_ideographic_plane,
455 supplementary_special_purpose_plane = 14,
456 private_use_plane_a,
457 private_use_plane_b,
458
459 bmp = basic_multilingual_plane,
460 smp = supplementary_multilingual_plane,
461 sip = supplementary_ideographic_plane,
462 tip = tertiary_ideographic_plane,
463 ssp = supplementary_special_purpose_plane,
464 spua_a = private_use_plane_a, pup_a = spua_a,
465 spua_b = private_use_plane_b, pup_b = spua_b,
466 };
467
468 constexpr inline char32_t last_unicode_code_point = 0x10FFFF;
469 constexpr inline char32_t first_unicode_high_surrogate = 0xD800;
470 constexpr inline char32_t last_unicode_high_surrogate = 0xDBFF;
471 constexpr inline char32_t first_unicode_low_surrogate = 0xDC00;
472 constexpr inline char32_t last_unicode_low_surrogate = 0xDFFF;
473
474 constexpr bool is_high_surrogate(char32_t cp) noexcept { return cp >= first_unicode_high_surrogate && cp <= last_unicode_high_surrogate; }
475 constexpr bool is_low_surrogate(char32_t cp) noexcept { return cp >= first_unicode_low_surrogate && cp <= last_unicode_low_surrogate; }
476 constexpr bool is_surrogate(char32_t cp) noexcept { return cp >= first_unicode_high_surrogate && cp <= last_unicode_low_surrogate; }
477 constexpr bool is_unicode(char32_t cp) noexcept { return cp <= last_unicode_code_point; }
478 constexpr auto get_unicode_plane(char32_t cp) noexcept -> unicode_plane { return is_unicode(cp) ? unicode_plane(cp >> 16) : unicode_plane::invalid; }
479 constexpr bool is_unicode_character(char32_t cp) noexcept { return is_unicode(cp) && ((cp & 0xFFFE) != 0xFFFE) && !(cp >= 0xFDD0 && cp <= 0xFDEF); }
480
481 constexpr char32_t surrogate_pair_to_codepoint(char32_t high, char32_t low) noexcept
482 {
483 return 0x10000 + ((high - first_unicode_high_surrogate) << 10) + (low - first_unicode_low_surrogate);
484 }
485
486 constexpr std::pair<char32_t, char32_t> codepoint_to_surrogate_pair(char32_t cp) noexcept
487 {
488 return { ((cp - 0x10000) >> 10) + first_unicode_high_surrogate, ((cp - 0x10000) & 0x3FF) + first_unicode_low_surrogate };
489 }
490
491 namespace detail
492 {
493 template <std::integral T>
494 T get_val(const void* source, std::endian source_endianness)
495 {
496 T result{};
497 std::memcpy(&result, source, sizeof(T));
498 if (std::endian::native != source_endianness)
499 return byteswap(result);
500 return result;
501 }
502 }
503
504 [[nodiscard]] constexpr size_t codepoint_utf8_count(char32_t cp) noexcept
505 {
506 constexpr size_t lut[33] = { 7,6,6,6,6,6,5,5,5,5,5,4,4,4,4,4,3,3,3,3,3,2,2,2,2,1,1,1,1,1,1,1,1 };
507 return lut[std::countl_zero(std::bit_cast<uint32_t>(cp))];
508 }
509
510 [[nodiscard]] constexpr size_t codepoint_utf8_count_from_end(stringable8 auto str)
511 {
512 auto sv = make_sv(str);
513 if (sv.empty())
514 return 0;
515 size_t result = 0;
516 uint8_t o;
517 do
518 {
519 o = std::bit_cast<uint8_t>(sv.back());
520 sv.remove_suffix(1);
521 result++;
522 } while ((o & 0xC0) == 0x80);
523 return result;
524 }
525
527 {
528 auto sv = make_sv(_str);
529 if (sv.empty())
530 return { text_decode_result::truncated };
531
532 switch (encoding.type)
533 {
534 case text_encoding_type::utf8:
535 {
536 const auto first = std::bit_cast<uint8_t>(sv[0]);
537 if (first < 0x80)
538 return { text_decode_result::valid, first, 1 };
539
540 char32_t value = 0;
541 uint8_t length = 0;
542 if ((first >> 5) == 0x6) { length = 2; value = first & 0x1F; }
543 else if ((first >> 4) == 0xe) { length = 3; value = first & 0xF; }
544 else if ((first >> 3) == 0x1e) { length = 4; value = first & 0x7; }
545
546 if (length == 0 || sv.size() < length)
547 return { text_decode_result::truncated, first, 1 };
548
549 switch (length)
550 {
551 case 4:
552 if ((sv[3] & 0xC0) != 0x80)
553 return { text_decode_result::invalid, first, 1 };
554 value = (value << 6) | (sv[3] & 0x3f);
555 [[fallthrough]];
556 case 3:
557 if ((sv[2] & 0xC0) != 0x80)
558 return { text_decode_result::invalid, first, 1 };
559 value = (value << 6) | (sv[2] & 0x3f);
560 [[fallthrough]];
561 case 2:
562 if ((sv[1] & 0xC0) != 0x80)
563 return { text_decode_result::invalid, first, 1 };
564 value = (value << 6) | (sv[1] & 0x3f);
565 return { text_decode_result::valid, value, length };
566 }
567 unreachable();
568 }
569 case text_encoding_type::utf16:
570 {
571 if (sv.size() < 2)
572 return { text_decode_result::truncated };
573
574 const uint16_t first = detail::get_val<uint16_t>(sv.data(), encoding.endianness);
575 if (is_surrogate(first))
576 {
577 if (is_high_surrogate(first))
578 {
579 if (sv.size() < 4)
580 return { text_decode_result::truncated, first, 2 };
581
582 const uint16_t second = detail::get_val<uint16_t>(sv.data() + 2, encoding.endianness);
583 if (is_low_surrogate(second))
584 return { text_decode_result::valid, surrogate_pair_to_codepoint(first, second), 4 };
585 }
586
587 return { text_decode_result::invalid, first, 2 };
588 }
589
590 return { text_decode_result::valid, first, 2 };
591 }
592 case text_encoding_type::utf32:
593 {
594 if (sv.size() < 4)
595 return { text_decode_result::truncated };
596
597 const uint32_t cp = detail::get_val<uint32_t>(sv.data(), encoding.endianness);
598 if (is_unicode(cp))
599 return { text_decode_result::valid, cp, 4 };
600 return { text_decode_result::invalid, cp, 4 };
601 }
602 default:
603 return {};
604 }
605 }
606
607 template <bytelike_range T>
608 [[nodiscard]] inline text_encoding detect_encoding(T const& str)
609 {
610 struct TextFileStats {
611 size_t points = 0;
612 size_t valid_points = 0;
613 size_t control_points = 0;
614 size_t plain_ascii = 0;
615 size_t whitespace = 0;
616 size_t extended_codepoints = 0;
617 float one_over_points = 0.f;
618 size_t invalid_points() const { return points - valid_points; }
619 float score() const { return (2.5f * whitespace + plain_ascii - 100.f * invalid_points() - 50.f * control_points + 5.f * extended_codepoints) * one_over_points; }
620 };
621
622 static const auto calculate_stats = [](TextFileStats& stats, bytelike_range auto sv, text_encoding encoding) {
623 size_t numBytes = 0;
624 while (!sv.empty())
625 {
626 text_decode_result decoded = decode_codepoint(sv, encoding);
627 if (decoded.status == text_decode_result::truncated)
628 break;
629 sv.remove_prefix(decoded.byte_count);
630 numBytes += decoded.byte_count;
631 stats.points++;
632 if (decoded.status == text_decode_result::valid)
633 {
634 stats.valid_points++;
635 if (decoded.point < 32)
636 {
637 if (decoded.point == '\n' || decoded.point == '\t')
638 {
639 stats.plain_ascii++;
640 stats.whitespace++;
641 }
642 else if (decoded.point == '\r')
643 stats.plain_ascii++;
644 else
645 stats.control_points++;
646 }
647 else if (decoded.point < 127)
648 {
649 stats.plain_ascii++;
650 if (decoded.point == ' ')
651 stats.whitespace++;
652 }
653 else if (decoded.point >= 65536)
654 stats.extended_codepoints++;
655 }
656 }
657 if (stats.points > 0)
658 stats.one_over_points = 1.f / stats.points;
659 return numBytes;
660 };
661
662 auto sv = make_sv(str);
663
664 if (sv.empty())
666
667 if (text_encoding encoding = consume_bom(sv); encoding != unknown_text_encoding)
668 return encoding;
669
670 sv = sv.substr(0, 4096);
671
672 TextFileStats stats8;
673
675 size_t numBytesRead = calculate_stats(stats8, sv, utf8_encoding);
676 if (numBytesRead == 0)
677 return utf8_encoding;
678
680 if (stats8.invalid_points() == 0 && stats8.control_points == 0)
681 return utf8_encoding;
682
685 text_encoding encoding8 = utf8_encoding;
686 if (const auto numHighBytes = numBytesRead - stats8.plain_ascii - stats8.control_points;
687 stats8.invalid_points() >= numHighBytes * 0.2f)
688 {
690 encoding8 = unknown_text_encoding;
691 stats8.points = numBytesRead;
692 stats8.valid_points = numBytesRead;
693 }
694
696 TextFileStats stats16_le;
697 calculate_stats(stats16_le, sv, utf16_le_encoding);
698
699 TextFileStats stats16_be;
700 calculate_stats(stats16_be, sv, utf16_be_encoding);
701
703 TextFileStats* stats16 = &stats16_le;
704 text_encoding encoding16 = utf16_le_encoding;
705 if (stats16_be.score() > stats16_le.score())
706 {
707 stats16 = &stats16_be;
708 encoding16 = utf16_be_encoding;
709 }
710
712 TextFileStats stats32_le;
713 calculate_stats(stats32_le, sv, utf32_le_encoding);
714
715 TextFileStats stats32_be;
716 calculate_stats(stats32_be, sv, utf32_be_encoding);
717
719 TextFileStats* stats32 = &stats32_le;
720 text_encoding encoding32 = utf32_le_encoding;
721 if (stats32_be.score() > stats32_le.score())
722 {
723 stats32 = &stats32_be;
724 encoding32 = utf32_be_encoding;
725 }
726
728 const auto score8 = stats8.score();
729 const auto score16 = stats16->score();
730 const auto score32 = stats32->score();
731 if (score8 >= score32)
732 {
733 if (score16 >= score8)
734 return encoding16;
735 return encoding8;
736 }
737
738 if (score32 >= score16)
739 return encoding32;
740 return encoding16;
741 }
742
743 [[gsl::suppress("type.1", "es.79")]]
744 [[nodiscard]] constexpr char32_t consume_utf8(string_view8 auto& str)
745 {
746 using char_type = typename std::remove_cvref_t<decltype(str)>::value_type;
747 using unsigned_char_type = std::make_unsigned_t<char_type>;
748
749 if (str.empty()) return 0;
750 auto it = std::to_address(str.begin());
751 char32_t cp = static_cast<unsigned_char_type>(*it);
752
753 int length = 0;
754 if (cp < 0x80) length = 1;
755 else if ((cp >> 5) == 0x6) length = 2;
756 else if ((cp >> 4) == 0xe) length = 3;
757 else if ((cp >> 3) == 0x1e) length = 4;
758 else return 0;
759
760 switch (length) {
761 case 2:
762 ++it; cp = ((cp << 6) & 0x7ff) + (static_cast<unsigned_char_type>(*it) & 0x3f);
763 break;
764 case 3:
765 ++it; cp = ((cp << 12) & 0xffff) + ((static_cast<unsigned_char_type>(*it) << 6) & 0xfff);
766 ++it; cp += static_cast<unsigned_char_type>(*it) & 0x3f;
767 break;
768 case 4:
769 ++it; cp = ((cp << 18) & 0x1fffff) + ((static_cast<unsigned_char_type>(*it) << 12) & 0x3ffff);
770 ++it; cp += (static_cast<unsigned_char_type>(*it) << 6) & 0xfff;
771 ++it; cp += static_cast<unsigned_char_type>(*it) & 0x3f;
772 break;
773 }
774 str.remove_prefix(length);
775 return cp;
776 }
777
778 [[gsl::suppress("type.1", "es.79")]]
779 [[nodiscard]] constexpr char32_t peek_utf8(string_view8 auto str)
780 {
781 using char_type = typename std::remove_cvref_t<decltype(str)>::value_type;
782 using unsigned_char_type = std::make_unsigned_t<char_type>;
783
784 if (str.empty()) return 0;
785 auto it = std::to_address(str.begin());
786 char32_t cp = static_cast<unsigned_char_type>(*it);
787
788 int length = 0;
789 if (cp < 0x80) length = 1;
790 else if ((cp >> 5) == 0x6) length = 2;
791 else if ((cp >> 4) == 0xe) length = 3;
792 else if ((cp >> 3) == 0x1e) length = 4;
793 else return 0;
794
795 switch (length) {
796 case 2:
797 ++it; cp = ((cp << 6) & 0x7ff) + (static_cast<unsigned_char_type>(*it) & 0x3f);
798 break;
799 case 3:
800 ++it; cp = ((cp << 12) & 0xffff) + ((static_cast<unsigned_char_type>(*it) << 6) & 0xfff);
801 ++it; cp += static_cast<unsigned_char_type>(*it) & 0x3f;
802 break;
803 case 4:
804 ++it; cp = ((cp << 18) & 0x1fffff) + ((static_cast<unsigned_char_type>(*it) << 12) & 0x3ffff);
805 ++it; cp += (static_cast<unsigned_char_type>(*it) << 6) & 0xfff;
806 ++it; cp += static_cast<unsigned_char_type>(*it) & 0x3f;
807 break;
808 }
809 return cp;
810 }
811
812 [[nodiscard]] constexpr size_t count_utf8_codepoints(stringable8 auto _str)
813 {
814 auto str = make_sv(_str);
815 using char_type = typename std::remove_cvref_t<decltype(str)>::value_type;
816 using unsigned_char_type = std::make_unsigned_t<char_type>;
817
818 auto it = std::to_address(str.begin());
819 const auto end = std::to_address(str.end());
820
821 size_t result = 0;
822 while (it < end)
823 {
824 char32_t cp = static_cast<unsigned_char_type>(*it);
825
826 int length = 1;
827 if ((cp >> 5) == 0x6) length = 2;
828 else if ((cp >> 4) == 0xe) length = 3;
829 else if ((cp >> 3) == 0x1e) length = 4;
830
831 it += length;
832 result++;
833 }
834 return result;
835 }
836
837 [[nodiscard]] constexpr size_t first_utf8_codepoint_size(stringable8 auto _str)
838 {
839 auto str = make_sv(_str);
840 using char_type = typename std::remove_cvref_t<decltype(str)>::value_type;
841 using unsigned_char_type = std::make_unsigned_t<char_type>;
842
843 auto it = std::to_address(str.begin());
844 const auto end = std::to_address(str.end());
845
846 if (it < end)
847 {
848 char32_t cp = static_cast<unsigned_char_type>(*it);
849
850 if ((cp >> 5) == 0x6) return 2;
851 else if ((cp >> 4) == 0xe) return 3;
852 else if ((cp >> 3) == 0x1e) return 4;
853 return 1;
854 }
855 return 0;
856 }
857
858
859 constexpr char32_t pop_utf8(string8 auto& str)
860 {
861 char32_t result = 0;
862 auto size = codepoint_utf8_count_from_end(str);
863 if (size > 0)
864 result = peek_utf8(make_sv(str).substr(str.size() - size));
865 str.resize(str.size() - size);
866 return result;
867 }
868
869 constexpr char32_t consume_utf8_at_end(string_view8 auto& str)
870 {
871 char32_t result = 0;
872 auto size = codepoint_utf8_count_from_end(str);
873 if (size > 0)
874 result = peek_utf8(str.substr(str.size() - size));
875 str.remove_suffix(size);
876 return result;
877 }
878
879 constexpr char32_t peek_utf8_at_end(string_view8 auto str)
880 {
881 char32_t result = 0;
882 auto size = codepoint_utf8_count_from_end(str);
883 if (size > 0)
884 result = peek_utf8(str.substr(str.size() - size));
885 return result;
886 }
887
888 [[gsl::suppress("type.1")]]
889 constexpr size_t append_utf8(string8 auto& buffer, char32_t cp)
890 {
891 using char_type = typename std::remove_cvref_t<decltype(buffer)>::value_type;
892#if 1
893 const size_t cp_bytes = codepoint_utf8_count(cp);
894 std::decay_t<decltype(buffer)> bytes(cp_bytes, 0);
895 switch (cp_bytes)
896 {
897 case 7: bytes[cp_bytes - 6] = static_cast<char_type>(0x80 | ((cp >> 30) & 0x3F)); [[fallthrough]];
898 case 6: bytes[cp_bytes - 5] = static_cast<char_type>(0x80 | ((cp >> 24) & 0x3F)); [[fallthrough]];
899 case 5: bytes[cp_bytes - 4] = static_cast<char_type>(0x80 | ((cp >> 18) & 0x3F)); [[fallthrough]];
900 case 4: bytes[cp_bytes - 3] = static_cast<char_type>(0x80 | ((cp >> 12) & 0x3F)); [[fallthrough]];
901 case 3: bytes[cp_bytes - 2] = static_cast<char_type>(0x80 | ((cp >> 6) & 0x3F)); [[fallthrough]];
902 case 2: bytes[cp_bytes - 1] = static_cast<char_type>(0x80 | ((cp >> 0) & 0x3F)); bytes[0] = static_cast<char_type>((std::uint_least16_t(0xFF00uL) >> cp_bytes) | (uint64_t(cp) >> (6 * cp_bytes - 6))); break;
903 case 1: bytes[0] = static_cast<char_type>(cp); break;
904 }
905 buffer += bytes;
906 return cp_bytes;
907#else
908 if (cp < 0x80)
909 {
910 buffer += static_cast<char_type>(cp);
911 return 1;
912 }
913 else if (cp < 0x800)
914 {
915 buffer += static_cast<char_type>((cp >> 6) | 0xc0);
916 buffer += static_cast<char_type>((cp & 0x3f) | 0x80);
917 return 2;
918 }
919 else if (cp < 0x10000)
920 {
921 buffer += static_cast<char_type>((cp >> 12) | 0xe0);
922 buffer += static_cast<char_type>(((cp >> 6) & 0x3f) | 0x80);
923 buffer += static_cast<char_type>((cp & 0x3f) | 0x80);
924 return 3;
925 }
926 else
927 {
928 buffer += static_cast<char_type>((cp >> 18) | 0xf0);
929 buffer += static_cast<char_type>(((cp >> 12) & 0x3f) | 0x80);
930 buffer += static_cast<char_type>(((cp >> 6) & 0x3f) | 0x80);
931 buffer += static_cast<char_type>((cp & 0x3f) | 0x80);
932 return 4;
933 }
934#endif
935 }
936
937 constexpr void transcode_codepage_to_utf8(string8 auto& dest, stringable8 auto source, std::span<char32_t const, 128> codepage_map)
938 {
939 using dest_char = typename std::decay_t<decltype(dest)>::value_type;
940 for (uint8_t cp : make_sv(source))
941 {
942 if (cp < 0x80)
943 dest += static_cast<dest_char>(cp);
944 else
945 append_utf8(dest, codepage_map[static_cast<size_t>(cp) - 0x80]);
946 }
947 }
948
949 template <typename T>
950 constexpr void transcode_codepage_to_unicode(T& dest, stringable8 auto source, std::span<char32_t const, 128> codepage_map)
951 {
952 using dest_char = typename std::decay_t<decltype(dest)>::value_type;
953 for (uint8_t cp : make_sv(source))
954 {
955 if (cp < 0x80)
956 dest += static_cast<dest_char>(cp);
957 else
958 append_codepoint(dest, codepage_map[static_cast<size_t>(cp) - 0x80]);
959 }
960 }
961
962 template <typename RESULT>
963 [[nodiscard]] constexpr auto transcode_codepage_to_unicode(stringable8 auto source, std::span<char32_t const, 128> codepage_map) -> RESULT
964 {
965 RESULT result{};
966 transcode_codepage_to_unicode(result, source, codepage_map);
967 return result;
968 }
969
970 template <string8 T>
971 constexpr auto transcode_codepage_to_utf8(stringable8 auto source, std::span<char32_t const, 128> codepage_map) -> T
972 {
973 T result{};
974 transcode_codepage_to_utf8(result, source, codepage_map);
975 return result;
976 }
977
978 [[gsl::suppress("type.1", "es.79")]]
979 [[nodiscard]] constexpr char32_t consume_utf16(string_view16 auto& str)
980 {
981 using char_type = typename std::remove_cvref_t<decltype(str)>::value_type;
982 using unsigned_char_type = std::make_unsigned_t<char_type const>;
983
984 if (str.empty()) return 0;
985 auto it = (unsigned_char_type*)std::to_address(str.begin());
986 char32_t cp = *it;
987
988 const int length = int(cp >= 0xD800 && cp <= 0xDBFF) + 1;
989
990 if (length == 2)
991 {
992 ++it;
993 cp = ((cp - 0xD800) << 10) | (*it - 0xDC00);
994 }
995 str.remove_prefix(length);
996 return cp;
997 }
998
999 [[nodiscard]] constexpr char32_t consume_utf32(string_view32 auto& str)
1000 {
1001 if (str.empty()) return 0;
1002 const auto result = str[0];
1003 str.remove_prefix(1);
1004 return result;
1005 }
1006
1007 [[gsl::suppress("type.1")]]
1008 constexpr size_t append_utf16(string16 auto& buffer, char32_t cp)
1009 {
1010 using char_type = typename std::remove_cvref_t<decltype(buffer)>::value_type;
1011 if (cp <= 0xFFFF)
1012 {
1013 buffer += static_cast<char_type>(cp);
1014 return 1;
1015 }
1016
1017 buffer += static_cast<char_type>((cp >> 10) + 0xD800);
1018 buffer += static_cast<char_type>((cp & 0x3FF) + 0xDC00);
1019 return 2;
1020 }
1021
1022 constexpr size_t append_utf32(string32 auto& buffer, char32_t cp)
1023 {
1024 buffer += cp;
1025 return 1;
1026 }
1027
1028 template <string8 T>
1029 [[gsl::suppress("type.1")]]
1030 [[nodiscard]] constexpr T to_utf8(char32_t cp)
1031 {
1032 using char_type = typename T::value_type;
1033 if (cp < 0x80)
1034 return { static_cast<char_type>(cp) };
1035 else if (cp < 0x800)
1036 return { static_cast<char_type>((cp >> 6) | 0xc0), static_cast<char_type>((cp & 0x3f) | 0x80) };
1037 else if (cp < 0x10000)
1038 return { static_cast<char_type>((cp >> 12) | 0xe0), static_cast<char_type>(((cp >> 6) & 0x3f) | 0x80), static_cast<char_type>((cp & 0x3f) | 0x80) };
1039 else
1040 return { static_cast<char_type>((cp >> 18) | 0xf0), static_cast<char_type>(((cp >> 12) & 0x3f) | 0x80), static_cast<char_type>(((cp >> 6) & 0x3f) | 0x80), static_cast<char_type>((cp & 0x3f) | 0x80) };
1041 }
1042
1043 template <string8 RESULT, stringable8 STR>
1044 [[nodiscard]] constexpr RESULT to_utf8(STR&& str)
1045 {
1046 if constexpr (std::same_as<STR, RESULT>)
1047 return std::forward<STR>(str);
1048 else
1049 {
1050 using char_type = typename RESULT::value_type;
1051 return RESULT{ string_view_cast<char_type>(make_sv(std::forward<STR>(str))) };
1052 }
1053 }
1054
1055 template <string8 RESULT, stringable16 STR>
1056 [[nodiscard]] constexpr RESULT to_utf8(STR&& str)
1057 {
1058 RESULT result{};
1059 auto sv = make_sv(str);
1060 while (!sv.empty())
1061 append_utf8(result, consume_utf16(sv));
1062 return result;
1063 }
1064
1066 template <string8 RESULT, stringable32 STR>
1067 [[nodiscard]] constexpr RESULT to_utf8(STR&& str)
1068 {
1069 RESULT result{};
1070 auto sv = make_sv(str);
1071 while (!sv.empty())
1072 append_utf8(result, consume_utf32(sv));
1073 return result;
1074 }
1075
1077 [[nodiscard]] inline std::string to_string(std::wstring_view str)
1078 {
1079 return to_utf8<std::string>(str);
1080 }
1081
1082 template <string16 T>
1083 [[gsl::suppress("type.1")]]
1084 [[nodiscard]] constexpr T to_utf16(char32_t cp)
1085 {
1086 using char_type = T::value_type;
1087 if (cp <= 0xFFFF)
1088 return { static_cast<char_type>(cp) };
1089 else
1090 return { static_cast<char_type>((cp >> 10) + 0xD800), static_cast<char_type>((cp & 0x3FF) + 0xDC00) };
1091 }
1092
1093 template <string16 T, stringable8 STR>
1094 [[nodiscard]] constexpr T to_utf16(STR str)
1095 {
1096 T result{};
1097 auto sv = make_sv(str);
1098 while (!sv.empty())
1099 append_utf16(result, consume_utf8(sv));
1100 return result;
1101 }
1102
1103 [[nodiscard]] inline std::wstring to_wstring(std::string_view str)
1104 {
1106 }
1107
1108 template <std::ranges::view R>
1109 struct utf8_view : public std::ranges::view_interface<utf8_view<R>>
1110 {
1111 template <typename RANGE_ITER, typename SENTINEL>
1112 struct utf8_iterator
1113 {
1114 using iterator_category = std::forward_iterator_tag;
1115 using value_type = char32_t;
1116 using difference_type = ptrdiff_t;
1117 using reference = char32_t;
1118
1119 constexpr utf8_iterator(RANGE_ITER current, SENTINEL end) : mCurrent(std::move(current)), mEnd(std::move(end)) {}
1120
1121 [[nodiscard]] constexpr value_type operator*() const {
1122 const auto length = len();
1123 auto it = mCurrent;
1124 char32_t cp = std::bit_cast<uint8_t>(*it);
1125
1126 switch (length) {
1127 case 2:
1128 ++it; cp = ((cp << 6) & 0x7ff) + ((*it) & 0x3f);
1129 break;
1130 case 3:
1131 ++it; cp = ((cp << 12) & 0xffff) + ((*it << 6) & 0xfff);
1132 ++it; cp += (*it) & 0x3f;
1133 break;
1134 case 4:
1135 ++it; cp = ((cp << 18) & 0x1fffff) + (((*it) << 12) & 0x3ffff);
1136 ++it; cp += ((*it) << 6) & 0xfff;
1137 ++it; cp += (*it) & 0x3f;
1138 break;
1139 }
1140 return cp;
1141 }
1142
1143 constexpr utf8_iterator& operator++() {
1144 std::advance(mCurrent, len());
1145 return *this;
1146 }
1147
1148 constexpr utf8_iterator operator++(int) {
1149 auto copy = *this;
1150 ++* this;
1151 return copy;
1152 }
1153
1154 constexpr auto operator<=>(utf8_iterator const&) const noexcept = default;
1155
1156 private:
1157
1158 size_t len() const
1159 {
1160 if (mCurrent >= mEnd)
1161 throw std::out_of_range("utf8 iterator out of range");
1162
1163 const unsigned cp = std::bit_cast<uint8_t>(*mCurrent);
1164 size_t length = 0;
1165 if (cp < 0x80) length = 1;
1166 else if ((cp >> 5) == 0x6) length = 2;
1167 else if ((cp >> 4) == 0xe) length = 3;
1168 else if ((cp >> 3) == 0x1e) length = 4;
1169 else
1170 throw std::runtime_error("invalid utf-8 prefix");
1171
1172 if (mCurrent + length > mEnd)
1173 throw std::runtime_error("utf-8 range contains codepoint with length beyond end of range");
1174
1175 return length;
1176 }
1177
1178 RANGE_ITER mCurrent;
1179 SENTINEL mEnd;
1180 };
1181
1182 utf8_view() = default;
1183
1184 constexpr utf8_view(R base)
1185 : mBase(base)
1186 {
1187 }
1188
1189 template <typename... ARGS>
1190 requires std::constructible_from<std::string_view, ARGS...>
1191 explicit constexpr utf8_view(ARGS&&... args)
1192 : mBase(std::forward<ARGS>(args)...)
1193 {
1194 }
1195
1196 constexpr R base() const&
1197 {
1198 return mBase;
1199 }
1200 constexpr R base()&&
1201 {
1202 return std::move(mBase);
1203 }
1204
1205 constexpr auto begin() const
1206 {
1207 return utf8_iterator<decltype(std::begin(mBase)), decltype(std::end(mBase))>{std::begin(mBase), std::end(mBase)};
1208 }
1209
1210 constexpr auto end() const
1211 {
1212 return utf8_iterator<decltype(std::end(mBase)), decltype(std::end(mBase))>{std::end(mBase), std::end(mBase)};
1213 }
1214
1215 private:
1216 R mBase{};
1217 };
1218
1219}
A range of bytelikes.
Definition bytes.h:35
Whether the type is a native char type.
Definition string_ops.h:100
The type is a string with a 16-bit char type.
Definition string_ops.h:61
The type is a string with an 32-bit char type.
Definition string_ops.h:71
The type is a string with an 8-bit char type.
Definition string_ops.h:51
The type is a string view with a 16-bit char type.
Definition string_ops.h:67
The type is a string view with a 32-bit char type.
Definition string_ops.h:77
The type is a string view with an 8-bit char type.
Definition string_ops.h:57
The type is convertible to a string view with a 16-bit char type.
Definition string_ops.h:64
The type is convertible to a string view with a 32-bit char type.
Definition string_ops.h:74
The type is convertible to a string view with an 8-bit char type.
Definition string_ops.h:54
auto as_bytes(std::span< FROM, N > bytes) noexcept
Returns a span of std::bytes that represents the bytelikes in bytes.
Definition bytes.h:95
constexpr std::basic_string_view< COUT > string_view_cast(std::basic_string_view< CIN > id) noexcept
Casts a string_view to a string_view with a different char type via a simple reinterpret_cast.
Definition string_ops.h:207
std::string_view substr(std::string_view str, intptr_t start, size_t count=std::string::npos) noexcept
Gets a substring of str starting at start and containing count characters.
Definition string_ops.h:593
std::string to_string(T floating_point)
float overload
unicode_plane
Represents the Unicode plane.
Definition unicode.h:448
constexpr char32_t consume_utf32(string_view32 auto &str)
Consumes (see consume()) a UTF-32 codepoint from str.
Definition unicode.h:999
text_encoding_type
Specifies a base text-encoding, ignoring endianness for multi-byte encodings.
Definition unicode.h:44
constexpr void transcode_unicode(FROM const &from, TO &out)
Converts a UTF-encoded string to a UTF-encoded string, of a different encoding. Decides the encodings...
Definition unicode.h:262
constexpr size_t codepoint_utf8_count_from_end(stringable8 auto str)
Count the number of octets in the last UTF-8 character in the string.
Definition unicode.h:510
constexpr char32_t peek_utf8(string_view8 auto str)
Decodes the first UTF-8 codepoint in str.
Definition unicode.h:779
constexpr void transcode_codepage_to_unicode(T &dest, stringable8 auto source, std::span< char32_t const, 128 > codepage_map)
Transcodes an Extended ASCII string source into unicode-encoded dest, according to codepage_map.
Definition unicode.h:950
constexpr bool is_unicode_character(char32_t cp) noexcept
Returns whether cp has a value that is a valid Unicode character (ie.
Definition unicode.h:479
constexpr size_t append_utf32(string32 auto &buffer, char32_t cp)
Appends 32-bit values to buffer by encoding cp into UTF-32.
Definition unicode.h:1022
constexpr char32_t surrogate_pair_to_codepoint(char32_t high, char32_t low) noexcept
Returns the codepoint encoded by two surrogates.
Definition unicode.h:481
constexpr size_t count_utf8_codepoints(stringable8 auto str)
Returns the number of codepoints in the given UTF-8 string str.
Definition unicode.h:812
constexpr bool is_unicode(char32_t cp) noexcept
Returns whether cp has a value that is a valid Unicode codepoint (ie. between 0 and 0x10FFFF).
Definition unicode.h:477
constexpr void transcode_codepage_to_utf8(string8 auto &dest, stringable8 auto source, std::span< char32_t const, 128 > codepage_map)
Transcodes an Extended ASCII string source into UTF-8 dest, according to codepage_map.
Definition unicode.h:937
constexpr size_t append_utf8(string8 auto &buffer, char32_t cp)
Appends octets to buffer by encoding cp into UTF-8.
Definition unicode.h:889
constexpr RESULT to_utf8(char32_t cp)
Returns cp encoded as a UTF-8 string.
Definition unicode.h:1030
constexpr char32_t consume_utf16(string_view16 auto &str)
Consumes (see consume()) a UTF-16 codepoint from str.
Definition unicode.h:979
constexpr RESULT to_utf16(char32_t cp)
Returns cp encoded as a UTF-16 string.
Definition unicode.h:1084
constexpr bool is_surrogate(char32_t cp) noexcept
Returns whether cp is a codepoint that encodes any part of a codepoint with a more-than-16-bit value.
Definition unicode.h:476
constexpr char32_t consume_utf8_at_end(string_view8 auto &str)
Consumes (see consume()) the last UTF-8 codepoint in str.
Definition unicode.h:869
constexpr void append_codepoint(T &str, char32_t cp)
Appends a codepoint to a UTF-encoded string. Supports UTF-8, UTF-16 and UTF-32, decides based on char...
Definition unicode.h:248
constexpr char32_t consume_codepoint(T &str)
Consumes a codepoint from a UTF-encoded string and returns it.
Definition unicode.h:232
constexpr char32_t consume_utf8(string_view8 auto &str)
Consumes (see consume()) a UTF-8 codepoint from str.
Definition unicode.h:744
constexpr size_t codepoint_utf8_count(char32_t cp) noexcept
Returns the number of UTF-8 octets necessarity to encode the given codepoint.
Definition unicode.h:504
constexpr bool is_high_surrogate(char32_t cp) noexcept
Returns whether cp is a codepoint that encodes the high part of a codepoint with a more-than-16-bit v...
Definition unicode.h:474
text_encoding detect_encoding(T const &range)
Attempts to detect the encoding of a given bytelike range.
Definition unicode.h:608
constexpr char32_t peek_utf8_at_end(string_view8 auto str)
Decodes the last UTF-8 codepoint in str.
Definition unicode.h:879
constexpr size_t append_utf16(string16 auto &buffer, char32_t cp)
Appends 16-bit values to buffer by encoding cp into UTF-16.
Definition unicode.h:1008
text_decode_result decode_codepoint(bytelike_range auto range, text_encoding encoding)
Attempts to decode the first codepoint in bytelike range range, assuming it is encoded in encoding.
Definition unicode.h:526
void encode_flag(std::string_view ascii_country_code, FUNC &&encoding_func)
Definition unicode.h:293
constexpr text_encoding unknown_text_encoding
Represents an unknown text encoding (e.g. when an encoding could not be determined).
Definition unicode.h:73
std::wstring to_wstring(std::string_view str)
Returns str (a UTF-8-encoded string) encoded as a UTF-16/32 string in a std::wstring (depending on th...
Definition unicode.h:1103
constexpr bool is_low_surrogate(char32_t cp) noexcept
Returns whether cp is a codepoint that encodes the low part of a codepoint with a more-than-16-bit va...
Definition unicode.h:475
text_encoding consume_bom(std::span< BYTE_TYPE, N > &spn)
Consumes (see consume()) a byte order mark from the beginning of spn (a span of bytelike),...
Definition unicode.h:328
constexpr size_t first_utf8_codepoint_size(stringable8 auto str)
Returns the size in octets of the first UTF-8 codepoint in str.
Definition unicode.h:837
constexpr char32_t pop_utf8(string8 auto &str)
Removes the last UTF-8 codepoint from str.
Definition unicode.h:859
@ invalid
Represents an invalid plane number.
Definition unicode.h:450
@ utf1
https://en.wikipedia.org/wiki/UTF-7
Definition unicode.h:47
@ utf_ebcdic
https://en.wikipedia.org/wiki/UTF-1
Definition unicode.h:48
@ gb18030
https://en.wikipedia.org/wiki/Binary_Ordered_Compression_for_Unicode
Definition unicode.h:51
@ scsu
https://en.wikipedia.org/wiki/UTF-EBCDIC
Definition unicode.h:49
@ bocu1
https://en.wikipedia.org/wiki/Standard_Compression_Scheme_for_Unicode
Definition unicode.h:50
Shamelessly stolen from https://github.com/arc80/plywood/.
Definition unicode.h:428
uint8_t byte_count
The number of bytes this codepoint takes up in the input string.
Definition unicode.h:441
char32_t point
The decoded codepoint (or -1 if failed).
Definition unicode.h:439
Type that represents a specific text encoding - a combination of ghassanpl::string_ops::text_encoding...
Definition unicode.h:56