mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 14:07:46 +00:00
AK: Add a Utf8View type for iterating over UTF-8 codepoints
Utf8View wraps a StringView and implements begin() and end() that return a Utf8CodepointIterator, which parses UTF-8-encoded Unicode codepoints and returns them as 32-bit integers. This is the first step towards supporting emojis in Serenity ^) https://github.com/SerenityOS/serenity/issues/490
This commit is contained in:
parent
970e0147f7
commit
5d3696174b
4 changed files with 241 additions and 1 deletions
130
AK/Utf8View.cpp
Normal file
130
AK/Utf8View.cpp
Normal file
|
@ -0,0 +1,130 @@
|
|||
#include <AK/Utf8View.h>
|
||||
|
||||
namespace AK {
|
||||
|
||||
Utf8View::Utf8View(const StringView& string)
|
||||
: m_string(string)
|
||||
{
|
||||
}
|
||||
|
||||
const unsigned char* Utf8View::begin_ptr() const
|
||||
{
|
||||
return (const unsigned char*)m_string.characters_without_null_termination();
|
||||
}
|
||||
|
||||
const unsigned char* Utf8View::end_ptr() const
|
||||
{
|
||||
return (const unsigned char*)m_string.characters_without_null_termination() + m_string.length();
|
||||
}
|
||||
|
||||
Utf8CodepointIterator Utf8View::begin() const
|
||||
{
|
||||
return { begin_ptr(), m_string.length() };
|
||||
}
|
||||
|
||||
Utf8CodepointIterator Utf8View::end() const
|
||||
{
|
||||
return { end_ptr(), 0 };
|
||||
}
|
||||
|
||||
static inline bool decode_first_byte(
|
||||
unsigned char byte,
|
||||
int& out_codepoint_length_in_bytes,
|
||||
u32& out_value)
|
||||
{
|
||||
if ((byte & 128) == 0) {
|
||||
out_value = byte;
|
||||
out_codepoint_length_in_bytes = 1;
|
||||
return true;
|
||||
}
|
||||
if ((byte & 64) == 0) {
|
||||
return false;
|
||||
}
|
||||
if ((byte & 32) == 0) {
|
||||
out_value = byte & 31;
|
||||
out_codepoint_length_in_bytes = 2;
|
||||
return true;
|
||||
}
|
||||
if ((byte & 16) == 0) {
|
||||
out_value = byte & 15;
|
||||
out_codepoint_length_in_bytes = 3;
|
||||
return true;
|
||||
}
|
||||
if ((byte & 8) == 0) {
|
||||
out_value = byte & 7;
|
||||
out_codepoint_length_in_bytes = 4;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Utf8View::validate() const
|
||||
{
|
||||
for (auto ptr = begin_ptr(); ptr < end_ptr(); ptr++) {
|
||||
int codepoint_length_in_bytes;
|
||||
u32 value;
|
||||
bool first_byte_makes_sense = decode_first_byte(*ptr, codepoint_length_in_bytes, value);
|
||||
if (!first_byte_makes_sense)
|
||||
return false;
|
||||
|
||||
for (int i = 1; i < codepoint_length_in_bytes; i++) {
|
||||
ptr++;
|
||||
if (ptr >= end_ptr())
|
||||
return false;
|
||||
if (*ptr >> 6 != 2)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Utf8CodepointIterator::Utf8CodepointIterator(const unsigned char* ptr, int length)
|
||||
: m_ptr(ptr)
|
||||
, m_length(length)
|
||||
{
|
||||
}
|
||||
|
||||
bool Utf8CodepointIterator::operator==(const Utf8CodepointIterator& other) const
|
||||
{
|
||||
return m_ptr == other.m_ptr && m_length == other.m_length;
|
||||
}
|
||||
|
||||
bool Utf8CodepointIterator::operator!=(const Utf8CodepointIterator& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
Utf8CodepointIterator& Utf8CodepointIterator::operator++()
|
||||
{
|
||||
do {
|
||||
ASSERT(m_length > 0);
|
||||
m_length--;
|
||||
m_ptr++;
|
||||
} while (m_ptr[0] >> 6 == 2);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
u32 Utf8CodepointIterator::operator*() const
|
||||
{
|
||||
ASSERT(m_length > 0);
|
||||
|
||||
u32 codepoint_value_so_far;
|
||||
int codepoint_length_in_bytes;
|
||||
|
||||
bool first_byte_makes_sense = decode_first_byte(m_ptr[0], codepoint_length_in_bytes, codepoint_value_so_far);
|
||||
ASSERT(first_byte_makes_sense);
|
||||
ASSERT(codepoint_length_in_bytes <= m_length);
|
||||
|
||||
for (int offset = 1; offset < codepoint_length_in_bytes; offset++) {
|
||||
ASSERT(m_ptr[offset] >> 6 == 2);
|
||||
codepoint_value_so_far <<= 6;
|
||||
codepoint_value_so_far |= m_ptr[offset] & 63;
|
||||
}
|
||||
|
||||
return codepoint_value_so_far;
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue