|  | // Copyright 2018 Google LLC. | 
|  | // Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. | 
|  |  | 
|  | #include "include/core/SkTypes.h" | 
|  | #include "src/base/SkUTF.h" | 
|  | #include "tests/Test.h" | 
|  |  | 
|  | #include <array> | 
|  | #include <cstdint> | 
|  | #include <cstddef> | 
|  | #include <cstring> | 
|  |  | 
|  | DEF_TEST(SkUTF_UTF16, reporter) { | 
|  | // Test non-basic-multilingual-plane unicode. | 
|  | static const SkUnichar gUni[] = { | 
|  | 0x10000, 0x18080, 0x20202, 0xFFFFF, 0x101234 | 
|  | }; | 
|  | for (SkUnichar uni : gUni) { | 
|  | uint16_t buf[2]; | 
|  | size_t count = SkUTF::ToUTF16(uni, buf); | 
|  | REPORTER_ASSERT(reporter, count == 2); | 
|  | size_t count2 = SkUTF::CountUTF16(buf, sizeof(buf)); | 
|  | REPORTER_ASSERT(reporter, count2 == 1); | 
|  | const uint16_t* ptr = buf; | 
|  | SkUnichar c = SkUTF::NextUTF16(&ptr, buf + std::size(buf)); | 
|  | REPORTER_ASSERT(reporter, c == uni); | 
|  | REPORTER_ASSERT(reporter, ptr - buf == 2); | 
|  | } | 
|  | } | 
|  |  | 
|  | DEF_TEST(SkUTF_UTF8, reporter) { | 
|  | static const struct { | 
|  | const char* fUtf8; | 
|  | SkUnichar   fUni; | 
|  | } gTest[] = { | 
|  | { "a",                  'a' }, | 
|  | { "\x7f",               0x7f }, | 
|  | { "\xC2\x80",           0x80 }, | 
|  | { "\xC3\x83",           (3 << 6) | 3    }, | 
|  | { "\xDF\xBF",           0x7ff }, | 
|  | { "\xE0\xA0\x80",       0x800 }, | 
|  | { "\xE0\xB0\xB8",       0xC38 }, | 
|  | { "\xE3\x83\x83",       (3 << 12) | (3 << 6) | 3    }, | 
|  | { "\xEF\xBF\xBF",       0xFFFF }, | 
|  | { "\xF0\x90\x80\x80",   0x10000 }, | 
|  | { "\xF3\x83\x83\x83",   (3 << 18) | (3 << 12) | (3 << 6) | 3    } | 
|  | }; | 
|  | for (auto test : gTest) { | 
|  | const char* p = test.fUtf8; | 
|  | const char* stop = p + strlen(p); | 
|  | int         n = SkUTF::CountUTF8(p, strlen(p)); | 
|  | SkUnichar   u1 = SkUTF::NextUTF8(&p, stop); | 
|  |  | 
|  | REPORTER_ASSERT(reporter, n == 1); | 
|  | REPORTER_ASSERT(reporter, u1 == test.fUni); | 
|  | REPORTER_ASSERT(reporter, p - test.fUtf8 == (int)strlen(test.fUtf8)); | 
|  | } | 
|  | } | 
|  |  | 
|  | #define ASCII_BYTE         "X" | 
|  | #define CONTINUATION_BYTE  "\xA1" | 
|  | #define LEADING_TWO_BYTE   "\xC2" | 
|  | #define LEADING_THREE_BYTE "\xE1" | 
|  | #define LEADING_FOUR_BYTE  "\xF0" | 
|  | #define INVALID_BYTE       "\xFC" | 
|  | DEF_TEST(SkUTF_CountUTF8, r) { | 
|  | static const struct { | 
|  | int expectedCount; | 
|  | const char* utf8String; | 
|  | } testCases[] = { | 
|  | { 0, "" }, | 
|  | { 1, ASCII_BYTE }, | 
|  | { 2, ASCII_BYTE ASCII_BYTE }, | 
|  | { 1, LEADING_TWO_BYTE CONTINUATION_BYTE }, | 
|  | { 2, ASCII_BYTE LEADING_TWO_BYTE CONTINUATION_BYTE }, | 
|  | { 3, ASCII_BYTE ASCII_BYTE LEADING_TWO_BYTE CONTINUATION_BYTE }, | 
|  | { 1, LEADING_THREE_BYTE CONTINUATION_BYTE CONTINUATION_BYTE }, | 
|  | { 2, ASCII_BYTE LEADING_THREE_BYTE CONTINUATION_BYTE CONTINUATION_BYTE }, | 
|  | { 3, ASCII_BYTE ASCII_BYTE LEADING_THREE_BYTE CONTINUATION_BYTE CONTINUATION_BYTE }, | 
|  | { 1, LEADING_FOUR_BYTE CONTINUATION_BYTE CONTINUATION_BYTE CONTINUATION_BYTE }, | 
|  | { 2, ASCII_BYTE LEADING_FOUR_BYTE CONTINUATION_BYTE CONTINUATION_BYTE CONTINUATION_BYTE }, | 
|  | { 3, ASCII_BYTE ASCII_BYTE LEADING_FOUR_BYTE CONTINUATION_BYTE CONTINUATION_BYTE | 
|  | CONTINUATION_BYTE }, | 
|  | { -1, INVALID_BYTE }, | 
|  | { -1, INVALID_BYTE CONTINUATION_BYTE }, | 
|  | { -1, INVALID_BYTE CONTINUATION_BYTE CONTINUATION_BYTE }, | 
|  | { -1, INVALID_BYTE CONTINUATION_BYTE CONTINUATION_BYTE CONTINUATION_BYTE }, | 
|  | { -1, LEADING_TWO_BYTE }, | 
|  | { -1, CONTINUATION_BYTE }, | 
|  | { -1, CONTINUATION_BYTE CONTINUATION_BYTE }, | 
|  | { -1, LEADING_THREE_BYTE CONTINUATION_BYTE }, | 
|  | { -1, CONTINUATION_BYTE CONTINUATION_BYTE CONTINUATION_BYTE }, | 
|  | { -1, LEADING_FOUR_BYTE CONTINUATION_BYTE }, | 
|  | { -1, CONTINUATION_BYTE CONTINUATION_BYTE CONTINUATION_BYTE CONTINUATION_BYTE }, | 
|  | }; | 
|  | for (auto testCase : testCases) { | 
|  | const char* str = testCase.utf8String; | 
|  | REPORTER_ASSERT(r, testCase.expectedCount == SkUTF::CountUTF8(str, strlen(str))); | 
|  | } | 
|  | } | 
|  |  | 
|  | DEF_TEST(SkUTF_NextUTF8_ToUTF8, r) { | 
|  | struct { | 
|  | SkUnichar expected; | 
|  | const char* utf8String; | 
|  | } testCases[] = { | 
|  | { -1, INVALID_BYTE }, | 
|  | { -1, "" }, | 
|  | { 0x0058, ASCII_BYTE }, | 
|  | { 0x00A1, LEADING_TWO_BYTE CONTINUATION_BYTE }, | 
|  | { 0x1861, LEADING_THREE_BYTE CONTINUATION_BYTE CONTINUATION_BYTE }, | 
|  | { 0x010330, LEADING_FOUR_BYTE "\x90\x8C\xB0" }, | 
|  | }; | 
|  | for (auto testCase : testCases) { | 
|  | const char* str = testCase.utf8String; | 
|  | SkUnichar uni = SkUTF::NextUTF8(&str, str + strlen(str)); | 
|  | REPORTER_ASSERT(r, str == testCase.utf8String + strlen(testCase.utf8String)); | 
|  | REPORTER_ASSERT(r, uni == testCase.expected); | 
|  | char buff[5] = {0, 0, 0, 0, 0}; | 
|  | size_t len = SkUTF::ToUTF8(uni, buff); | 
|  | if (buff[len] != 0) { | 
|  | ERRORF(r, "unexpected write"); | 
|  | continue; | 
|  | } | 
|  | if (uni == -1) { | 
|  | REPORTER_ASSERT(r, len == 0); | 
|  | continue; | 
|  | } | 
|  | if (len == 0) { | 
|  | ERRORF(r, "unexpected failure."); | 
|  | continue; | 
|  | } | 
|  | if (len > 4) { | 
|  | ERRORF(r, "wrote too much"); | 
|  | continue; | 
|  | } | 
|  | str = testCase.utf8String; | 
|  | REPORTER_ASSERT(r, len == strlen(buff)); | 
|  | REPORTER_ASSERT(r, len == strlen(str)); | 
|  | REPORTER_ASSERT(r, 0 == strcmp(str, buff)); | 
|  | } | 
|  | } | 
|  | #undef ASCII_BYTE | 
|  | #undef CONTINUATION_BYTE | 
|  | #undef LEADING_TWO_BYTE | 
|  | #undef LEADING_THREE_BYTE | 
|  | #undef LEADING_FOUR_BYTE | 
|  | #undef INVALID_BYTE |