diff options
-rw-r--r-- | src/core/SkString.cpp | 46 | ||||
-rw-r--r-- | src/ports/SkOSFile_stdio.cpp | 50 | ||||
-rw-r--r-- | tests/StringTest.cpp | 48 |
3 files changed, 96 insertions, 48 deletions
diff --git a/src/core/SkString.cpp b/src/core/SkString.cpp index 1242b3333d..d388f18ec7 100644 --- a/src/core/SkString.cpp +++ b/src/core/SkString.cpp @@ -378,30 +378,34 @@ void SkString::setUTF16(const uint16_t src[]) { this->setUTF16(src, count); } -void SkString::setUTF16(const uint16_t src[], size_t count) { - count = trim_size_t_to_u32(count); - - if (0 == count) { - this->reset(); - } else if (count <= fRec->fLength) { - // should we resize if len <<<< fLength, to save RAM? (e.g. len < (fLength>>1)) - if (count < fRec->fLength) { - this->resize(count); - } - char* p = this->writable_str(); - for (size_t i = 0; i < count; i++) { - p[i] = SkToU8(src[i]); +static SkString utf8_from_utf16(const uint16_t src[], size_t count) { + SkString ret; + if (count > 0) { + SkASSERT(src); + size_t n = 0; + const uint16_t* end = src + count; + for (const uint16_t* ptr = src; ptr < end;) { + const uint16_t* last = ptr; + SkUnichar u = SkUTF16_NextUnichar(&ptr); + size_t s = SkUTF8_FromUnichar(u); + if (n > SK_MaxU32 - s) { + end = last; // truncate input string + break; + } + n += s; } - p[count] = 0; - } else { - SkString tmp(count); // puts a null terminator at the end of the string - char* p = tmp.writable_str(); - - for (size_t i = 0; i < count; i++) { - p[i] = SkToU8(src[i]); + ret = SkString(n); + char* out = ret.writable_str(); + for (const uint16_t* ptr = src; ptr < end;) { + out += SkUTF8_FromUnichar(SkUTF16_NextUnichar(&ptr), out); } - this->swap(tmp); + SkASSERT(out == ret.writable_str() + n); } + return ret; +} + +void SkString::setUTF16(const uint16_t src[], size_t count) { + *this = utf8_from_utf16(src, count); } void SkString::insert(size_t offset, const char text[]) { diff --git a/src/ports/SkOSFile_stdio.cpp b/src/ports/SkOSFile_stdio.cpp index 7cdc549df6..d8541c21c0 100644 --- a/src/ports/SkOSFile_stdio.cpp +++ b/src/ports/SkOSFile_stdio.cpp @@ -19,14 +19,53 @@ #ifdef _WIN32 #include <direct.h> #include <io.h> +#include <vector> +#include "SkUtils.h" #endif #ifdef SK_BUILD_FOR_IOS #include "SkOSFile_ios.h" #endif +#ifdef _WIN32 +static bool is_ascii(const char* s) { + while (char v = *s++) { + if ((v & 0x80) != 0) { + return false; + } + } + return true; +} + +static FILE* fopen_win(const char* utf8path, const char* perm) { + if (is_ascii(utf8path)) { + return fopen(utf8path, perm); + } + + const char* ptr = utf8path; + const char* end = utf8path + strlen(utf8path); + size_t n = 0; + while (ptr < end) { + SkUnichar u = SkUTF8_NextUnicharWithError(&ptr, end); + if (u < 0) { + return nullptr; // malformed UTF-8 + } + n += SkUTF16_FromUnichar(u); + } + std::vector<uint16_t> wchars(n + 1); + uint16_t* out = wchars.data(); + for (const char* ptr = utf8path; ptr < end;) { + out += SkUTF16_FromUnichar(SkUTF8_NextUnicharWithError(&ptr, end), out); + } + SkASSERT(out == &wchars[n]); + *out = 0; // final null + wchar_t wperms[4] = {(wchar_t)perm[0], (wchar_t)perm[1], (wchar_t)perm[2], (wchar_t)perm[3]}; + return _wfopen((wchar_t*)wchars.data(), wperms); +} +#endif + FILE* sk_fopen(const char path[], SkFILE_Flags flags) { - char perm[4]; + char perm[4] = {0, 0, 0, 0}; char* p = perm; if (flags & kRead_SkFILE_Flag) { @@ -35,13 +74,14 @@ FILE* sk_fopen(const char path[], SkFILE_Flags flags) { if (flags & kWrite_SkFILE_Flag) { *p++ = 'w'; } - *p++ = 'b'; - *p = 0; + *p = 'b'; - //TODO: on Windows fopen is just ASCII or the current code page, - //convert to utf16 and use _wfopen FILE* file = nullptr; +#ifdef _WIN32 + file = fopen_win(path, perm); +#else file = fopen(path, perm); +#endif #ifdef SK_BUILD_FOR_IOS // if not found in default path and read-only, try to open from bundle if (!file && kRead_SkFILE_Flag == flags) { diff --git a/tests/StringTest.cpp b/tests/StringTest.cpp index f9f76e9f21..5ff24125b0 100644 --- a/tests/StringTest.cpp +++ b/tests/StringTest.cpp @@ -11,27 +11,6 @@ #include "Test.h" #include <thread> -// Windows vsnprintf doesn't 0-terminate safely), but is so far -// encapsulated in SkString that we can't test it directly. - -#ifdef SK_BUILD_FOR_WIN - #define VSNPRINTF(buffer, size, format, args) \ - vsnprintf_s(buffer, size, _TRUNCATE, format, args) -#else - #define VSNPRINTF vsnprintf -#endif - -#define ARGS_TO_BUFFER(format, buffer, size) \ - do { \ - va_list args; \ - va_start(args, format); \ - VSNPRINTF(buffer, size, format, args); \ - va_end(args); \ - } while (0) - -static void printfAnalog(char* buffer, int size, const char format[], ...) { - ARGS_TO_BUFFER(format, buffer, size); -} DEF_TEST(String, reporter) { SkString a; @@ -188,7 +167,7 @@ DEF_TEST(String, reporter) { REPORTER_ASSERT(reporter, buffer[18] == 'a'); REPORTER_ASSERT(reporter, buffer[19] == 'a'); REPORTER_ASSERT(reporter, buffer[20] == 'a'); - printfAnalog(buffer, 20, "%30d", 0); + snprintf(buffer, 20, "%30d", 0); REPORTER_ASSERT(reporter, buffer[18] == ' '); REPORTER_ASSERT(reporter, buffer[19] == 0); REPORTER_ASSERT(reporter, buffer[20] == 'a'); @@ -325,3 +304,28 @@ DEF_TEST(String_huge, r) { } } +static SkString utf16_to_utf8(const uint16_t* utf16, size_t len) { + SkString s; + s.setUTF16(utf16, len); + return s; +} + +DEF_TEST(String_fromUTF16, r) { + // test data produced with `iconv`. + const uint16_t test1[] = { + 0xD835, 0xDCD0, 0xD835, 0xDCD1, 0xD835, 0xDCD2, 0xD835, 0xDCD3, 0xD835, 0xDCD4, 0x0020, + 0xD835, 0xDCD5, 0xD835, 0xDCD6, 0xD835, 0xDCD7, 0xD835, 0xDCD8, 0xD835, 0xDCD9 + }; + REPORTER_ASSERT(r, utf16_to_utf8(test1, SK_ARRAY_COUNT(test1)).equals("πππππ πππππ")); + + const uint16_t test2[] = { + 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0020, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, + }; + REPORTER_ASSERT(r, utf16_to_utf8(test2, SK_ARRAY_COUNT(test2)).equals("ABCDE FGHIJ")); + + const uint16_t test3[] = { + 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x0020, 0x03B6, 0x03B7, 0x03B8, 0x03B9, 0x03BA, + }; + REPORTER_ASSERT(r, utf16_to_utf8(test3, SK_ARRAY_COUNT(test3)).equals("αβγδΡ ΞΆΞ·ΞΈΞΉΞΊ")); +} + |