libutils/Unicode.cpp: Correct length computation and add checks for utf16->utf8

am: 3411dd4457

Change-Id: I34322210bd7ab90c1986d55f9b444292e76c646a
This commit is contained in:
Sergio Giro 2016-07-14 18:07:28 +00:00 committed by android-build-merger
commit 01dbd1da64
4 changed files with 45 additions and 18 deletions

View file

@ -87,7 +87,7 @@ ssize_t utf32_to_utf8_length(const char32_t *src, size_t src_len);
* "dst" becomes \xE3\x81\x82\xE3\x81\x84 * "dst" becomes \xE3\x81\x82\xE3\x81\x84
* (note that "dst" is NOT null-terminated, like strncpy) * (note that "dst" is NOT null-terminated, like strncpy)
*/ */
void utf32_to_utf8(const char32_t* src, size_t src_len, char* dst); void utf32_to_utf8(const char32_t* src, size_t src_len, char* dst, size_t dst_len);
/** /**
* Returns the unicode value at "index". * Returns the unicode value at "index".
@ -109,7 +109,7 @@ ssize_t utf16_to_utf8_length(const char16_t *src, size_t src_len);
* enough to fit the UTF-16 as measured by utf16_to_utf8_length with an added * enough to fit the UTF-16 as measured by utf16_to_utf8_length with an added
* NULL terminator. * NULL terminator.
*/ */
void utf16_to_utf8(const char16_t* src, size_t src_len, char* dst); void utf16_to_utf8(const char16_t* src, size_t src_len, char* dst, size_t dst_len);
/** /**
* Returns the length of "src" when "src" is valid UTF-8 string. * Returns the length of "src" when "src" is valid UTF-8 string.

View file

@ -102,20 +102,21 @@ static char* allocFromUTF16(const char16_t* in, size_t len)
{ {
if (len == 0) return getEmptyString(); if (len == 0) return getEmptyString();
const ssize_t bytes = utf16_to_utf8_length(in, len); // Allow for closing '\0'
if (bytes < 0) { const ssize_t resultStrLen = utf16_to_utf8_length(in, len) + 1;
if (resultStrLen < 1) {
return getEmptyString(); return getEmptyString();
} }
SharedBuffer* buf = SharedBuffer::alloc(bytes+1); SharedBuffer* buf = SharedBuffer::alloc(resultStrLen);
ALOG_ASSERT(buf, "Unable to allocate shared buffer"); ALOG_ASSERT(buf, "Unable to allocate shared buffer");
if (!buf) { if (!buf) {
return getEmptyString(); return getEmptyString();
} }
char* str = (char*)buf->data(); char* resultStr = (char*)buf->data();
utf16_to_utf8(in, len, str); utf16_to_utf8(in, len, resultStr, resultStrLen);
return str; return resultStr;
} }
static char* allocFromUTF32(const char32_t* in, size_t len) static char* allocFromUTF32(const char32_t* in, size_t len)
@ -124,21 +125,21 @@ static char* allocFromUTF32(const char32_t* in, size_t len)
return getEmptyString(); return getEmptyString();
} }
const ssize_t bytes = utf32_to_utf8_length(in, len); const ssize_t resultStrLen = utf32_to_utf8_length(in, len) + 1;
if (bytes < 0) { if (resultStrLen < 1) {
return getEmptyString(); return getEmptyString();
} }
SharedBuffer* buf = SharedBuffer::alloc(bytes+1); SharedBuffer* buf = SharedBuffer::alloc(resultStrLen);
ALOG_ASSERT(buf, "Unable to allocate shared buffer"); ALOG_ASSERT(buf, "Unable to allocate shared buffer");
if (!buf) { if (!buf) {
return getEmptyString(); return getEmptyString();
} }
char* str = (char*) buf->data(); char* resultStr = (char*) buf->data();
utf32_to_utf8(in, len, str); utf32_to_utf8(in, len, resultStr, resultStrLen);
return str; return resultStr;
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------

View file

@ -14,6 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
#include <log/log.h>
#include <utils/Unicode.h> #include <utils/Unicode.h>
#include <stddef.h> #include <stddef.h>
@ -182,7 +183,7 @@ ssize_t utf32_to_utf8_length(const char32_t *src, size_t src_len)
return ret; return ret;
} }
void utf32_to_utf8(const char32_t* src, size_t src_len, char* dst) void utf32_to_utf8(const char32_t* src, size_t src_len, char* dst, size_t dst_len)
{ {
if (src == NULL || src_len == 0 || dst == NULL) { if (src == NULL || src_len == 0 || dst == NULL) {
return; return;
@ -193,9 +194,12 @@ void utf32_to_utf8(const char32_t* src, size_t src_len, char* dst)
char *cur = dst; char *cur = dst;
while (cur_utf32 < end_utf32) { while (cur_utf32 < end_utf32) {
size_t len = utf32_codepoint_utf8_length(*cur_utf32); size_t len = utf32_codepoint_utf8_length(*cur_utf32);
LOG_ALWAYS_FATAL_IF(dst_len < len, "%zu < %zu", dst_len, len);
utf32_codepoint_to_utf8((uint8_t *)cur, *cur_utf32++, len); utf32_codepoint_to_utf8((uint8_t *)cur, *cur_utf32++, len);
cur += len; cur += len;
dst_len -= len;
} }
LOG_ALWAYS_FATAL_IF(dst_len < 1, "dst_len < 1: %zu < 1", dst_len);
*cur = '\0'; *cur = '\0';
} }
@ -324,7 +328,7 @@ int strzcmp16_h_n(const char16_t *s1H, size_t n1, const char16_t *s2N, size_t n2
: 0); : 0);
} }
void utf16_to_utf8(const char16_t* src, size_t src_len, char* dst) void utf16_to_utf8(const char16_t* src, size_t src_len, char* dst, size_t dst_len)
{ {
if (src == NULL || src_len == 0 || dst == NULL) { if (src == NULL || src_len == 0 || dst == NULL) {
return; return;
@ -345,9 +349,12 @@ void utf16_to_utf8(const char16_t* src, size_t src_len, char* dst)
utf32 = (char32_t) *cur_utf16++; utf32 = (char32_t) *cur_utf16++;
} }
const size_t len = utf32_codepoint_utf8_length(utf32); const size_t len = utf32_codepoint_utf8_length(utf32);
LOG_ALWAYS_FATAL_IF(dst_len < len, "%zu < %zu", dst_len, len);
utf32_codepoint_to_utf8((uint8_t*)cur, utf32, len); utf32_codepoint_to_utf8((uint8_t*)cur, utf32, len);
cur += len; cur += len;
dst_len -= len;
} }
LOG_ALWAYS_FATAL_IF(dst_len < 1, "%zu < 1", dst_len);
*cur = '\0'; *cur = '\0';
} }
@ -408,10 +415,10 @@ ssize_t utf16_to_utf8_length(const char16_t *src, size_t src_len)
const char16_t* const end = src + src_len; const char16_t* const end = src + src_len;
while (src < end) { while (src < end) {
if ((*src & 0xFC00) == 0xD800 && (src + 1) < end if ((*src & 0xFC00) == 0xD800 && (src + 1) < end
&& (*++src & 0xFC00) == 0xDC00) { && (*(src + 1) & 0xFC00) == 0xDC00) {
// surrogate pairs are always 4 bytes. // surrogate pairs are always 4 bytes.
ret += 4; ret += 4;
src++; src += 2;
} else { } else {
ret += utf32_codepoint_utf8_length((char32_t) *src++); ret += utf32_codepoint_utf8_length((char32_t) *src++);
} }

View file

@ -17,6 +17,7 @@
#define LOG_TAG "String8_test" #define LOG_TAG "String8_test"
#include <utils/Log.h> #include <utils/Log.h>
#include <utils/String8.h> #include <utils/String8.h>
#include <utils/String16.h>
#include <gtest/gtest.h> #include <gtest/gtest.h>
@ -72,4 +73,22 @@ TEST_F(String8Test, OperatorPlusEquals) {
EXPECT_STREQ(src3, " Verify me."); EXPECT_STREQ(src3, " Verify me.");
} }
// http://b/29250543
TEST_F(String8Test, CorrectInvalidSurrogate) {
// d841d8 is an invalid start for a surrogate pair. Make sure this is handled by ignoring the
// first character in the pair and handling the rest correctly.
String16 string16(u"\xd841\xd841\xdc41\x0000");
String8 string8(string16);
EXPECT_EQ(4U, string8.length());
}
TEST_F(String8Test, CheckUtf32Conversion) {
// Since bound checks were added, check the conversion can be done without fatal errors.
// The utf8 lengths of these are chars are 1 + 2 + 3 + 4 = 10.
const char32_t string32[] = U"\x0000007f\x000007ff\x0000911\x0010fffe";
String8 string8(string32);
EXPECT_EQ(10U, string8.length());
}
} }