#pragma once
#include <string>
namespace whiteout::common::unicode {
inline constexpr char32_t kReplacementCharacter = 0xFFFD;
inline constexpr char32_t kMaxCodePoint = 0x10FFFF;
inline constexpr char32_t kFirstSupplementary = 0x10000;
inline constexpr char16_t kHighSurrogateFirst = 0xD800;
inline constexpr char16_t kHighSurrogateLast = 0xDBFF;
inline constexpr char16_t kLowSurrogateFirst = 0xDC00;
inline constexpr char16_t kLowSurrogateLast = 0xDFFF;
inline constexpr unsigned kSurrogatePayloadBits = 10;
constexpr bool isHighSurrogate(char16_t unit) {
return unit >= kHighSurrogateFirst && unit <= kHighSurrogateLast;
}
constexpr bool isLowSurrogate(char16_t unit) {
return unit >= kLowSurrogateFirst && unit <= kLowSurrogateLast;
}
constexpr bool isSurrogate(char32_t codePoint) {
return codePoint >= static_cast<char32_t>(kHighSurrogateFirst) &&
codePoint <= static_cast<char32_t>(kLowSurrogateLast);
}
constexpr char32_t combineSurrogates(char16_t high, char16_t low) {
return kFirstSupplementary +
(static_cast<char32_t>(high - kHighSurrogateFirst) << kSurrogatePayloadBits) +
static_cast<char32_t>(low - kLowSurrogateFirst);
}
inline void appendUtf8(std::string& out, char32_t codePoint) {
struct Length {
unsigned payloadBits; unsigned char leadTag; };
static constexpr Length kLengths[] = {
{7, 0b0000'0000}, // 0xxxxxxx
{11, 0b1100'0000}, {16, 0b1110'0000}, // 1110xxxx 10xxxxxx 10xxxxxx
{21, 0b1111'0000}, };
constexpr unsigned kContinuationPayloadBits = 6;
constexpr unsigned char kContinuationTag = 0b1000'0000;
constexpr char32_t kContinuationPayloadMask = 0b0011'1111;
if (isSurrogate(codePoint) || codePoint > kMaxCodePoint)
codePoint = kReplacementCharacter;
unsigned continuations = 0;
while (codePoint >> kLengths[continuations].payloadBits)
++continuations;
out += static_cast<char>(kLengths[continuations].leadTag |
(codePoint >> (continuations * kContinuationPayloadBits)));
while (continuations-- > 0) {
char32_t const payload = codePoint >> (continuations * kContinuationPayloadBits);
out += static_cast<char>(kContinuationTag | (payload & kContinuationPayloadMask));
}
}
}