namespace cloverfield
{
namespace detail
{
inline int utf32_to_utf8(char* s, char32_t c32)
{
int result = -1;
if (c32 == 0)
{
s[0] = '\0';
result = 1;
}
else if (c32 < 0x80)
{
*s = static_cast<char>(c32);
result = 1;
}
else if (c32 < 0x800)
{
s[0] = static_cast<char>(0xc0 | (c32 >> 6));
s[1] = static_cast<char>(0x80 | (c32 & 0x3f));
result = 2;
}
else if (c32 < 0x10000)
{
s[0] = static_cast<char>(0xe0 | (c32 >> 12));
s[1] = static_cast<char>(0x80 | ((c32 >> 6) & 0x3f));
s[2] = static_cast<char>(0x80 | (c32 & 0x3f));
result = 3;
}
else if (c32 < 0x110000)
{
s[0] = static_cast<char>(0xf0 | (c32 >> 18));
s[1] = static_cast<char>(0x80 | ((c32 >> 12) & 0x3f));
s[2] = static_cast<char>(0x80 | ((c32 >> 6) & 0x3f));
s[3] = static_cast<char>(0x80 | (c32 & 0x3f));
result = 4;
}
return result;
}
}
constexpr int mb_len_max = 4;
constexpr int mb_cur_max = 4;
struct mbstate_t
{
std::uint32_t state;
};
inline std::size_t ctrtomb(char* s, char32_t c32, mbstate_t* ps)
{
if (ps == nullptr)
{
thread_local mbstate_t ts{};
ps = &ts;
}
if (s == nullptr)
{
static char buf[mb_len_max];
s = buf;
c32 = 0;
}
int t;
std::size_t result = -1;
if (c32 != 0 && (ps->state & 0xfc00) == 0xd800)
goto illegal_sequence;
t = detail::utf32_to_utf8(s, c32);
if (t < 0)
goto illegal_sequence;
result = t;
ps->state = c32;
return result;
illegal_sequence:
ps->state = -1;
throw std::invalid_argument("cloverfield::ctrtomb");
}
inline std::size_t ctrtomb(char* s, char16_t c16, mbstate_t* ps)
{
if (ps == nullptr)
{
thread_local mbstate_t ts{};
ps = &ts;
}
if (s == nullptr)
{
static char buf[mb_len_max];
s = buf;
c16 = 0;
}
int t;
std::size_t result = -1;
char32_t c32 = c16;
if (c32 != 0)
{
if ((ps->state & 0xfc00) == 0xd800)
{
if ((c16 & 0xfc00) == 0xdc00)
c32 = ((ps->state & 0x3ff) << 10 | c16 & 0x3ff) + 0x10000;
else
goto illegal_sequence;
}
else if ((c16 & 0xfc00) == 0xd800)
{
ps->state = c16;
return 0;
}
}
t = detail::utf32_to_utf8(s, c32);
if (t < 0)
goto illegal_sequence;
result = t;
ps->state = c32;
return result;
illegal_sequence:
ps->state = -1;
throw std::invalid_argument("cloverfield::ctrtomb");
}
}