diff --git a/ion/Makefile b/ion/Makefile index b2ea8e4ba..1c3e616a4 100644 --- a/ion/Makefile +++ b/ion/Makefile @@ -34,8 +34,9 @@ tests += $(addprefix ion/test/,\ keyboard.cpp\ storage.cpp\ utf8_decoder.cpp\ + utf8_helper.cpp\ ) ifdef ION_STORAGE_LOG SFLAGS += -DION_STORAGE_LOG=1 -endif \ No newline at end of file +endif diff --git a/ion/include/ion/unicode/utf8_helper.h b/ion/include/ion/unicode/utf8_helper.h index 2a665f25c..cfb15c128 100644 --- a/ion/include/ion/unicode/utf8_helper.h +++ b/ion/include/ion/unicode/utf8_helper.h @@ -68,6 +68,10 @@ int RemovePreviousCodePoint(const char * text, char * location, CodePoint * c); const char * CodePointAtGlyphOffset(const char * buffer, int position); size_t GlyphOffsetAtCodePoint(const char * buffer, const char * position); +/* Return the number of glyphs in a string. + * For instance, strlen("∑") = 3 but StringGlyphLength("∑") = 1 */ +size_t StringGlyphLength(const char * s, int maxSize = -1); + }; #endif diff --git a/ion/src/shared/unicode/utf8_helper.cpp b/ion/src/shared/unicode/utf8_helper.cpp index 51a8bf3ac..0eeb12c9b 100644 --- a/ion/src/shared/unicode/utf8_helper.cpp +++ b/ion/src/shared/unicode/utf8_helper.cpp @@ -326,5 +326,20 @@ size_t GlyphOffsetAtCodePoint(const char * buffer, const char * position) { return glyphIndex; } +size_t StringGlyphLength(const char * s, int maxSize) { + if (maxSize == 0) { + return 0; + } + UTF8Decoder decoder(s); + CodePoint codePoint = decoder.nextCodePoint(); + size_t glyphIndex = 0; + while (codePoint != UCodePointNull && (maxSize < 0 || ((decoder.stringPosition() - s) <= maxSize))) { + if (!codePoint.isCombining()) { + glyphIndex++; + } + codePoint = decoder.nextCodePoint(); + } + return glyphIndex; +} -}; +} diff --git a/ion/test/utf8_helper.cpp b/ion/test/utf8_helper.cpp new file mode 100644 index 000000000..975b5cff1 --- /dev/null +++ b/ion/test/utf8_helper.cpp @@ -0,0 +1,14 @@ +#include +#include + +void assert_string_glyph_length_is(const char * string, int maxSize, size_t result) { + quiz_assert(UTF8Helper::StringGlyphLength(string, maxSize) == result); +} + +QUIZ_CASE(ion_utf8_helper_glyph_length) { + assert_string_glyph_length_is("123", -1, 3); + assert_string_glyph_length_is("1ᴇ3", -1, 3); + assert_string_glyph_length_is("∑∫𝐢", -1, 3); + assert_string_glyph_length_is("123", 2, 2); + assert_string_glyph_length_is("1ᴇ3", 2, 1); +}