Reland "libutils: Introduce StaticString16""
This reverts commit 1270e4fbf1
.
Bug: 138856262
Test: Run unit tests.
Change-Id: I37be01d7d4f98a83078870cb0917275336fa2bbd
Merged-In: I8da93f2c9b95183e32d4a2ea895f90c449abbe4d
This commit is contained in:
parent
8bea425845
commit
9fb93edd5b
6 changed files with 448 additions and 50 deletions
|
@ -205,6 +205,7 @@ cc_test {
|
|||
"Mutex_test.cpp",
|
||||
"SharedBuffer_test.cpp",
|
||||
"String8_test.cpp",
|
||||
"String16_test.cpp",
|
||||
"StrongPointer_test.cpp",
|
||||
"Unicode_test.cpp",
|
||||
"Vector_test.cpp",
|
||||
|
|
|
@ -41,6 +41,7 @@ SharedBuffer* SharedBuffer::alloc(size_t size)
|
|||
// The following is OK on Android-supported platforms.
|
||||
sb->mRefs.store(1, std::memory_order_relaxed);
|
||||
sb->mSize = size;
|
||||
sb->mClientMetadata = 0;
|
||||
}
|
||||
return sb;
|
||||
}
|
||||
|
|
|
@ -102,7 +102,12 @@ private:
|
|||
// Must be sized to preserve correct alignment.
|
||||
mutable std::atomic<int32_t> mRefs;
|
||||
size_t mSize;
|
||||
uint32_t mReserved[2];
|
||||
uint32_t mReserved;
|
||||
public:
|
||||
// mClientMetadata is reserved for client use. It is initialized to 0
|
||||
// and the clients can do whatever they want with it. Note that this is
|
||||
// placed last so that it is adjcent to the buffer allocated.
|
||||
uint32_t mClientMetadata;
|
||||
};
|
||||
|
||||
static_assert(sizeof(SharedBuffer) % 8 == 0
|
||||
|
|
|
@ -24,21 +24,21 @@
|
|||
|
||||
namespace android {
|
||||
|
||||
static const StaticString16 emptyString(u"");
|
||||
static inline char16_t* getEmptyString() {
|
||||
static SharedBuffer* gEmptyStringBuf = [] {
|
||||
SharedBuffer* buf = SharedBuffer::alloc(sizeof(char16_t));
|
||||
char16_t* str = static_cast<char16_t*>(buf->data());
|
||||
*str = 0;
|
||||
return buf;
|
||||
}();
|
||||
|
||||
gEmptyStringBuf->acquire();
|
||||
return static_cast<char16_t*>(gEmptyStringBuf->data());
|
||||
return const_cast<char16_t*>(emptyString.string());
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
static char16_t* allocFromUTF8(const char* u8str, size_t u8len)
|
||||
void* String16::alloc(size_t size)
|
||||
{
|
||||
SharedBuffer* buf = SharedBuffer::alloc(size);
|
||||
buf->mClientMetadata = kIsSharedBufferAllocated;
|
||||
return buf;
|
||||
}
|
||||
|
||||
char16_t* String16::allocFromUTF8(const char* u8str, size_t u8len)
|
||||
{
|
||||
if (u8len == 0) return getEmptyString();
|
||||
|
||||
|
@ -49,7 +49,7 @@ static char16_t* allocFromUTF8(const char* u8str, size_t u8len)
|
|||
return getEmptyString();
|
||||
}
|
||||
|
||||
SharedBuffer* buf = SharedBuffer::alloc(sizeof(char16_t)*(u16len+1));
|
||||
SharedBuffer* buf = static_cast<SharedBuffer*>(alloc(sizeof(char16_t) * (u16len + 1)));
|
||||
if (buf) {
|
||||
u8cur = (const uint8_t*) u8str;
|
||||
char16_t* u16str = (char16_t*)buf->data();
|
||||
|
@ -66,13 +66,13 @@ static char16_t* allocFromUTF8(const char* u8str, size_t u8len)
|
|||
return getEmptyString();
|
||||
}
|
||||
|
||||
static char16_t* allocFromUTF16(const char16_t* u16str, size_t u16len) {
|
||||
char16_t* String16::allocFromUTF16(const char16_t* u16str, size_t u16len) {
|
||||
if (u16len >= SIZE_MAX / sizeof(char16_t)) {
|
||||
android_errorWriteLog(0x534e4554, "73826242");
|
||||
abort();
|
||||
}
|
||||
|
||||
SharedBuffer* buf = SharedBuffer::alloc((u16len + 1) * sizeof(char16_t));
|
||||
SharedBuffer* buf = static_cast<SharedBuffer*>(alloc((u16len + 1) * sizeof(char16_t)));
|
||||
ALOG_ASSERT(buf, "Unable to allocate shared buffer");
|
||||
if (buf) {
|
||||
char16_t* str = (char16_t*)buf->data();
|
||||
|
@ -97,8 +97,8 @@ String16::String16(StaticLinkage)
|
|||
// having run. In this case we always allocate an empty string. It's less
|
||||
// efficient than using getEmptyString(), but we assume it's uncommon.
|
||||
|
||||
char16_t* data = static_cast<char16_t*>(
|
||||
SharedBuffer::alloc(sizeof(char16_t))->data());
|
||||
SharedBuffer* buf = static_cast<SharedBuffer*>(alloc(sizeof(char16_t)));
|
||||
char16_t* data = static_cast<char16_t*>(buf->data());
|
||||
data[0] = 0;
|
||||
mString = data;
|
||||
}
|
||||
|
@ -106,7 +106,7 @@ String16::String16(StaticLinkage)
|
|||
String16::String16(const String16& o)
|
||||
: mString(o.mString)
|
||||
{
|
||||
SharedBuffer::bufferFromData(mString)->acquire();
|
||||
acquire();
|
||||
}
|
||||
|
||||
String16::String16(const String16& o, size_t len, size_t begin)
|
||||
|
@ -136,26 +136,30 @@ String16::String16(const char* o, size_t len)
|
|||
|
||||
String16::~String16()
|
||||
{
|
||||
SharedBuffer::bufferFromData(mString)->release();
|
||||
release();
|
||||
}
|
||||
|
||||
size_t String16::size() const
|
||||
{
|
||||
return SharedBuffer::sizeFromData(mString)/sizeof(char16_t)-1;
|
||||
if (isStaticString()) {
|
||||
return staticStringSize();
|
||||
} else {
|
||||
return SharedBuffer::sizeFromData(mString) / sizeof(char16_t) - 1;
|
||||
}
|
||||
}
|
||||
|
||||
void String16::setTo(const String16& other)
|
||||
{
|
||||
SharedBuffer::bufferFromData(other.mString)->acquire();
|
||||
SharedBuffer::bufferFromData(mString)->release();
|
||||
release();
|
||||
mString = other.mString;
|
||||
acquire();
|
||||
}
|
||||
|
||||
status_t String16::setTo(const String16& other, size_t len, size_t begin)
|
||||
{
|
||||
const size_t N = other.size();
|
||||
if (begin >= N) {
|
||||
SharedBuffer::bufferFromData(mString)->release();
|
||||
release();
|
||||
mString = getEmptyString();
|
||||
return OK;
|
||||
}
|
||||
|
@ -184,8 +188,7 @@ status_t String16::setTo(const char16_t* other, size_t len)
|
|||
abort();
|
||||
}
|
||||
|
||||
SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
|
||||
->editResize((len+1)*sizeof(char16_t));
|
||||
SharedBuffer* buf = static_cast<SharedBuffer*>(editResize((len + 1) * sizeof(char16_t)));
|
||||
if (buf) {
|
||||
char16_t* str = (char16_t*)buf->data();
|
||||
memmove(str, other, len*sizeof(char16_t));
|
||||
|
@ -212,8 +215,8 @@ status_t String16::append(const String16& other)
|
|||
abort();
|
||||
}
|
||||
|
||||
SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
|
||||
->editResize((myLen+otherLen+1)*sizeof(char16_t));
|
||||
SharedBuffer* buf =
|
||||
static_cast<SharedBuffer*>(editResize((myLen + otherLen + 1) * sizeof(char16_t)));
|
||||
if (buf) {
|
||||
char16_t* str = (char16_t*)buf->data();
|
||||
memcpy(str+myLen, other, (otherLen+1)*sizeof(char16_t));
|
||||
|
@ -238,8 +241,8 @@ status_t String16::append(const char16_t* chrs, size_t otherLen)
|
|||
abort();
|
||||
}
|
||||
|
||||
SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
|
||||
->editResize((myLen+otherLen+1)*sizeof(char16_t));
|
||||
SharedBuffer* buf =
|
||||
static_cast<SharedBuffer*>(editResize((myLen + otherLen + 1) * sizeof(char16_t)));
|
||||
if (buf) {
|
||||
char16_t* str = (char16_t*)buf->data();
|
||||
memcpy(str+myLen, chrs, otherLen*sizeof(char16_t));
|
||||
|
@ -273,8 +276,8 @@ status_t String16::insert(size_t pos, const char16_t* chrs, size_t len)
|
|||
len, myLen, String8(chrs, len).string());
|
||||
#endif
|
||||
|
||||
SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
|
||||
->editResize((myLen+len+1)*sizeof(char16_t));
|
||||
SharedBuffer* buf =
|
||||
static_cast<SharedBuffer*>(editResize((myLen + len + 1) * sizeof(char16_t)));
|
||||
if (buf) {
|
||||
char16_t* str = (char16_t*)buf->data();
|
||||
if (pos < myLen) {
|
||||
|
@ -338,23 +341,87 @@ bool String16::contains(const char16_t* chrs) const
|
|||
return strstr16(mString, chrs) != nullptr;
|
||||
}
|
||||
|
||||
void* String16::edit() {
|
||||
SharedBuffer* buf;
|
||||
if (isStaticString()) {
|
||||
buf = static_cast<SharedBuffer*>(alloc((size() + 1) * sizeof(char16_t)));
|
||||
if (buf) {
|
||||
buf->acquire();
|
||||
memcpy(buf->data(), mString, (size() + 1) * sizeof(char16_t));
|
||||
}
|
||||
} else {
|
||||
buf = SharedBuffer::bufferFromData(mString)->edit();
|
||||
buf->mClientMetadata = kIsSharedBufferAllocated;
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
void* String16::editResize(size_t newSize) {
|
||||
SharedBuffer* buf;
|
||||
if (isStaticString()) {
|
||||
size_t copySize = (size() + 1) * sizeof(char16_t);
|
||||
if (newSize < copySize) {
|
||||
copySize = newSize;
|
||||
}
|
||||
buf = static_cast<SharedBuffer*>(alloc(newSize));
|
||||
if (buf) {
|
||||
buf->acquire();
|
||||
memcpy(buf->data(), mString, copySize);
|
||||
}
|
||||
} else {
|
||||
buf = SharedBuffer::bufferFromData(mString)->editResize(newSize);
|
||||
buf->mClientMetadata = kIsSharedBufferAllocated;
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
void String16::acquire()
|
||||
{
|
||||
if (!isStaticString()) {
|
||||
SharedBuffer::bufferFromData(mString)->acquire();
|
||||
}
|
||||
}
|
||||
|
||||
void String16::release()
|
||||
{
|
||||
if (!isStaticString()) {
|
||||
SharedBuffer::bufferFromData(mString)->release();
|
||||
}
|
||||
}
|
||||
|
||||
bool String16::isStaticString() const {
|
||||
// See String16.h for notes on the memory layout of String16::StaticData and
|
||||
// SharedBuffer.
|
||||
static_assert(sizeof(SharedBuffer) - offsetof(SharedBuffer, mClientMetadata) == 4);
|
||||
const uint32_t* p = reinterpret_cast<const uint32_t*>(mString);
|
||||
return (*(p - 1) & kIsSharedBufferAllocated) == 0;
|
||||
}
|
||||
|
||||
size_t String16::staticStringSize() const {
|
||||
// See String16.h for notes on the memory layout of String16::StaticData and
|
||||
// SharedBuffer.
|
||||
static_assert(sizeof(SharedBuffer) - offsetof(SharedBuffer, mClientMetadata) == 4);
|
||||
const uint32_t* p = reinterpret_cast<const uint32_t*>(mString);
|
||||
return static_cast<size_t>(*(p - 1));
|
||||
}
|
||||
|
||||
status_t String16::makeLower()
|
||||
{
|
||||
const size_t N = size();
|
||||
const char16_t* str = string();
|
||||
char16_t* edit = nullptr;
|
||||
char16_t* edited = nullptr;
|
||||
for (size_t i=0; i<N; i++) {
|
||||
const char16_t v = str[i];
|
||||
if (v >= 'A' && v <= 'Z') {
|
||||
if (!edit) {
|
||||
SharedBuffer* buf = SharedBuffer::bufferFromData(mString)->edit();
|
||||
if (!edited) {
|
||||
SharedBuffer* buf = static_cast<SharedBuffer*>(edit());
|
||||
if (!buf) {
|
||||
return NO_MEMORY;
|
||||
}
|
||||
edit = (char16_t*)buf->data();
|
||||
mString = str = edit;
|
||||
edited = (char16_t*)buf->data();
|
||||
mString = str = edited;
|
||||
}
|
||||
edit[i] = tolower((char)v);
|
||||
edited[i] = tolower((char)v);
|
||||
}
|
||||
}
|
||||
return OK;
|
||||
|
@ -364,18 +431,18 @@ status_t String16::replaceAll(char16_t replaceThis, char16_t withThis)
|
|||
{
|
||||
const size_t N = size();
|
||||
const char16_t* str = string();
|
||||
char16_t* edit = nullptr;
|
||||
char16_t* edited = nullptr;
|
||||
for (size_t i=0; i<N; i++) {
|
||||
if (str[i] == replaceThis) {
|
||||
if (!edit) {
|
||||
SharedBuffer* buf = SharedBuffer::bufferFromData(mString)->edit();
|
||||
if (!edited) {
|
||||
SharedBuffer* buf = static_cast<SharedBuffer*>(edit());
|
||||
if (!buf) {
|
||||
return NO_MEMORY;
|
||||
}
|
||||
edit = (char16_t*)buf->data();
|
||||
mString = str = edit;
|
||||
edited = (char16_t*)buf->data();
|
||||
mString = str = edited;
|
||||
}
|
||||
edit[i] = withThis;
|
||||
edited[i] = withThis;
|
||||
}
|
||||
}
|
||||
return OK;
|
||||
|
@ -385,7 +452,7 @@ status_t String16::remove(size_t len, size_t begin)
|
|||
{
|
||||
const size_t N = size();
|
||||
if (begin >= N) {
|
||||
SharedBuffer::bufferFromData(mString)->release();
|
||||
release();
|
||||
mString = getEmptyString();
|
||||
return OK;
|
||||
}
|
||||
|
@ -395,8 +462,7 @@ status_t String16::remove(size_t len, size_t begin)
|
|||
}
|
||||
|
||||
if (begin > 0) {
|
||||
SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
|
||||
->editResize((N+1)*sizeof(char16_t));
|
||||
SharedBuffer* buf = static_cast<SharedBuffer*>(editResize((N + 1) * sizeof(char16_t)));
|
||||
if (!buf) {
|
||||
return NO_MEMORY;
|
||||
}
|
||||
|
@ -404,8 +470,7 @@ status_t String16::remove(size_t len, size_t begin)
|
|||
memmove(str, str+begin, (N-begin+1)*sizeof(char16_t));
|
||||
mString = str;
|
||||
}
|
||||
SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
|
||||
->editResize((len+1)*sizeof(char16_t));
|
||||
SharedBuffer* buf = static_cast<SharedBuffer*>(editResize((len + 1) * sizeof(char16_t)));
|
||||
if (buf) {
|
||||
char16_t* str = (char16_t*)buf->data();
|
||||
str[len] = 0;
|
||||
|
|
218
libutils/String16_test.cpp
Normal file
218
libutils/String16_test.cpp
Normal file
|
@ -0,0 +1,218 @@
|
|||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <utils/String16.h>
|
||||
#include <utils/String8.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
::testing::AssertionResult Char16_tStringEquals(const char16_t* a, const char16_t* b) {
|
||||
if (strcmp16(a, b) != 0) {
|
||||
return ::testing::AssertionFailure()
|
||||
<< "\"" << String8(a).c_str() << "\" not equal to \"" << String8(b).c_str() << "\"";
|
||||
}
|
||||
return ::testing::AssertionSuccess();
|
||||
}
|
||||
|
||||
#define EXPECT_STR16EQ(a, b) EXPECT_TRUE(Char16_tStringEquals(a, b))
|
||||
|
||||
TEST(String16Test, FromChar16_t) {
|
||||
String16 tmp(u"Verify me");
|
||||
EXPECT_STR16EQ(u"Verify me", tmp);
|
||||
}
|
||||
|
||||
TEST(String16Test, FromChar16_tSized) {
|
||||
String16 tmp(u"Verify me", 7);
|
||||
EXPECT_STR16EQ(u"Verify ", tmp);
|
||||
}
|
||||
|
||||
TEST(String16Test, FromChar) {
|
||||
String16 tmp("Verify me");
|
||||
EXPECT_STR16EQ(u"Verify me", tmp);
|
||||
}
|
||||
|
||||
TEST(String16Test, FromCharSized) {
|
||||
String16 tmp("Verify me", 7);
|
||||
EXPECT_STR16EQ(u"Verify ", tmp);
|
||||
}
|
||||
|
||||
TEST(String16Test, Copy) {
|
||||
String16 tmp("Verify me");
|
||||
String16 another = tmp;
|
||||
EXPECT_STR16EQ(u"Verify me", tmp);
|
||||
EXPECT_STR16EQ(u"Verify me", another);
|
||||
}
|
||||
|
||||
TEST(String16Test, Move) {
|
||||
String16 tmp("Verify me");
|
||||
String16 another(std::move(tmp));
|
||||
EXPECT_STR16EQ(u"Verify me", another);
|
||||
}
|
||||
|
||||
TEST(String16Test, Size) {
|
||||
String16 tmp("Verify me");
|
||||
EXPECT_EQ(9U, tmp.size());
|
||||
}
|
||||
|
||||
TEST(String16Test, setTo) {
|
||||
String16 tmp("Verify me");
|
||||
tmp.setTo(u"New content");
|
||||
EXPECT_EQ(11U, tmp.size());
|
||||
EXPECT_STR16EQ(u"New content", tmp);
|
||||
}
|
||||
|
||||
TEST(String16Test, Append) {
|
||||
String16 tmp("Verify me");
|
||||
tmp.append(String16("Hello"));
|
||||
EXPECT_EQ(14U, tmp.size());
|
||||
EXPECT_STR16EQ(u"Verify meHello", tmp);
|
||||
}
|
||||
|
||||
TEST(String16Test, Insert) {
|
||||
String16 tmp("Verify me");
|
||||
tmp.insert(6, u"Insert");
|
||||
EXPECT_EQ(15U, tmp.size());
|
||||
EXPECT_STR16EQ(u"VerifyInsert me", tmp);
|
||||
}
|
||||
|
||||
TEST(String16Test, Remove) {
|
||||
String16 tmp("Verify me");
|
||||
tmp.remove(2, 6);
|
||||
EXPECT_EQ(2U, tmp.size());
|
||||
EXPECT_STR16EQ(u" m", tmp);
|
||||
}
|
||||
|
||||
TEST(String16Test, MakeLower) {
|
||||
String16 tmp("Verify Me!");
|
||||
tmp.makeLower();
|
||||
EXPECT_EQ(10U, tmp.size());
|
||||
EXPECT_STR16EQ(u"verify me!", tmp);
|
||||
}
|
||||
|
||||
TEST(String16Test, ReplaceAll) {
|
||||
String16 tmp("Verify verify Verify");
|
||||
tmp.replaceAll(u'r', u'!');
|
||||
EXPECT_STR16EQ(u"Ve!ify ve!ify Ve!ify", tmp);
|
||||
}
|
||||
|
||||
TEST(String16Test, Compare) {
|
||||
String16 tmp("Verify me");
|
||||
EXPECT_EQ(String16(u"Verify me"), tmp);
|
||||
}
|
||||
|
||||
TEST(String16Test, StaticString) {
|
||||
String16 nonStaticString("NonStatic");
|
||||
StaticString16 staticString(u"Static");
|
||||
|
||||
EXPECT_TRUE(staticString.isStaticString());
|
||||
EXPECT_FALSE(nonStaticString.isStaticString());
|
||||
}
|
||||
|
||||
TEST(String16Test, StaticStringCopy) {
|
||||
StaticString16 tmp(u"Verify me");
|
||||
String16 another = tmp;
|
||||
EXPECT_STR16EQ(u"Verify me", tmp);
|
||||
EXPECT_STR16EQ(u"Verify me", another);
|
||||
EXPECT_TRUE(tmp.isStaticString());
|
||||
EXPECT_TRUE(another.isStaticString());
|
||||
}
|
||||
|
||||
TEST(String16Test, StaticStringMove) {
|
||||
StaticString16 tmp(u"Verify me");
|
||||
String16 another(std::move(tmp));
|
||||
EXPECT_STR16EQ(u"Verify me", another);
|
||||
EXPECT_TRUE(another.isStaticString());
|
||||
}
|
||||
|
||||
TEST(String16Test, StaticStringSize) {
|
||||
StaticString16 tmp(u"Verify me");
|
||||
EXPECT_EQ(9U, tmp.size());
|
||||
}
|
||||
|
||||
TEST(String16Test, StaticStringSetTo) {
|
||||
StaticString16 tmp(u"Verify me");
|
||||
tmp.setTo(u"New content");
|
||||
EXPECT_EQ(11U, tmp.size());
|
||||
EXPECT_STR16EQ(u"New content", tmp);
|
||||
EXPECT_FALSE(tmp.isStaticString());
|
||||
}
|
||||
|
||||
TEST(String16Test, StaticStringAppend) {
|
||||
StaticString16 tmp(u"Verify me");
|
||||
tmp.append(String16("Hello"));
|
||||
EXPECT_EQ(14U, tmp.size());
|
||||
EXPECT_STR16EQ(u"Verify meHello", tmp);
|
||||
EXPECT_FALSE(tmp.isStaticString());
|
||||
}
|
||||
|
||||
TEST(String16Test, StaticStringInsert) {
|
||||
StaticString16 tmp(u"Verify me");
|
||||
tmp.insert(6, u"Insert");
|
||||
EXPECT_EQ(15U, tmp.size());
|
||||
EXPECT_STR16EQ(u"VerifyInsert me", tmp);
|
||||
EXPECT_FALSE(tmp.isStaticString());
|
||||
}
|
||||
|
||||
TEST(String16Test, StaticStringRemove) {
|
||||
StaticString16 tmp(u"Verify me");
|
||||
tmp.remove(2, 6);
|
||||
EXPECT_EQ(2U, tmp.size());
|
||||
EXPECT_STR16EQ(u" m", tmp);
|
||||
EXPECT_FALSE(tmp.isStaticString());
|
||||
}
|
||||
|
||||
TEST(String16Test, StaticStringMakeLower) {
|
||||
StaticString16 tmp(u"Verify me!");
|
||||
tmp.makeLower();
|
||||
EXPECT_EQ(10U, tmp.size());
|
||||
EXPECT_STR16EQ(u"verify me!", tmp);
|
||||
EXPECT_FALSE(tmp.isStaticString());
|
||||
}
|
||||
|
||||
TEST(String16Test, StaticStringReplaceAll) {
|
||||
StaticString16 tmp(u"Verify verify Verify");
|
||||
tmp.replaceAll(u'r', u'!');
|
||||
EXPECT_STR16EQ(u"Ve!ify ve!ify Ve!ify", tmp);
|
||||
EXPECT_FALSE(tmp.isStaticString());
|
||||
}
|
||||
|
||||
TEST(String16Test, StaticStringCompare) {
|
||||
StaticString16 tmp(u"Verify me");
|
||||
EXPECT_EQ(String16(u"Verify me"), tmp);
|
||||
}
|
||||
|
||||
TEST(String16Test, StringSetToStaticString) {
|
||||
StaticString16 tmp(u"Verify me");
|
||||
String16 another(u"nonstatic");
|
||||
another = tmp;
|
||||
EXPECT_STR16EQ(u"Verify me", tmp);
|
||||
EXPECT_STR16EQ(u"Verify me", another);
|
||||
}
|
||||
|
||||
TEST(String16Test, StringMoveFromStaticString) {
|
||||
StaticString16 tmp(u"Verify me");
|
||||
String16 another(std::move(tmp));
|
||||
EXPECT_STR16EQ(u"Verify me", another);
|
||||
}
|
||||
|
||||
TEST(String16Test, EmptyStringIsStatic) {
|
||||
String16 tmp("");
|
||||
EXPECT_TRUE(tmp.isStaticString());
|
||||
}
|
||||
|
||||
} // namespace android
|
|
@ -37,13 +37,17 @@ namespace android {
|
|||
|
||||
class String8;
|
||||
|
||||
template <size_t N>
|
||||
class StaticString16;
|
||||
|
||||
// DO NOT USE: please use std::u16string
|
||||
|
||||
//! This is a string holding UTF-16 characters.
|
||||
class String16
|
||||
{
|
||||
public:
|
||||
/* use String16(StaticLinkage) if you're statically linking against
|
||||
/*
|
||||
* Use String16(StaticLinkage) if you're statically linking against
|
||||
* libutils and declaring an empty static String16, e.g.:
|
||||
*
|
||||
* static String16 sAStaticEmptyString(String16::kEmptyString);
|
||||
|
@ -123,14 +127,118 @@ public:
|
|||
|
||||
inline operator const char16_t*() const;
|
||||
|
||||
private:
|
||||
const char16_t* mString;
|
||||
// Static and non-static String16 behave the same for the users, so
|
||||
// this method isn't of much use for the users. It is public for testing.
|
||||
bool isStaticString() const;
|
||||
|
||||
private:
|
||||
/*
|
||||
* A flag indicating the type of underlying buffer.
|
||||
*/
|
||||
static constexpr uint32_t kIsSharedBufferAllocated = 0x80000000;
|
||||
|
||||
/*
|
||||
* alloc() returns void* so that SharedBuffer class is not exposed.
|
||||
*/
|
||||
static void* alloc(size_t size);
|
||||
static char16_t* allocFromUTF8(const char* u8str, size_t u8len);
|
||||
static char16_t* allocFromUTF16(const char16_t* u16str, size_t u16len);
|
||||
|
||||
/*
|
||||
* edit() and editResize() return void* so that SharedBuffer class
|
||||
* is not exposed.
|
||||
*/
|
||||
void* edit();
|
||||
void* editResize(size_t new_size);
|
||||
|
||||
void acquire();
|
||||
void release();
|
||||
|
||||
size_t staticStringSize() const;
|
||||
|
||||
const char16_t* mString;
|
||||
|
||||
protected:
|
||||
/*
|
||||
* Data structure used to allocate static storage for static String16.
|
||||
*
|
||||
* Note that this data structure and SharedBuffer are used interchangably
|
||||
* as the underlying data structure for a String16. Therefore, the layout
|
||||
* of this data structure must match the part in SharedBuffer that is
|
||||
* visible to String16.
|
||||
*/
|
||||
template <size_t N>
|
||||
struct StaticData {
|
||||
// The high bit of 'size' is used as a flag.
|
||||
static_assert(N - 1 < kIsSharedBufferAllocated, "StaticString16 too long!");
|
||||
constexpr StaticData() : size(N - 1), data{0} {}
|
||||
const uint32_t size;
|
||||
char16_t data[N];
|
||||
|
||||
constexpr StaticData(const StaticData<N>&) = default;
|
||||
};
|
||||
|
||||
/*
|
||||
* Helper function for constructing a StaticData object.
|
||||
*/
|
||||
template <size_t N>
|
||||
static constexpr const StaticData<N> makeStaticData(const char16_t (&s)[N]) {
|
||||
StaticData<N> r;
|
||||
// The 'size' field is at the same location where mClientMetadata would
|
||||
// be for a SharedBuffer. We do NOT set kIsSharedBufferAllocated flag
|
||||
// here.
|
||||
for (size_t i = 0; i < N - 1; ++i) r.data[i] = s[i];
|
||||
return r;
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
explicit constexpr String16(const StaticData<N>& s) : mString(s.data) {}
|
||||
|
||||
public:
|
||||
template <size_t N>
|
||||
explicit constexpr String16(const StaticString16<N>& s) : mString(s.mString) {}
|
||||
};
|
||||
|
||||
// String16 can be trivially moved using memcpy() because moving does not
|
||||
// require any change to the underlying SharedBuffer contents or reference count.
|
||||
ANDROID_TRIVIAL_MOVE_TRAIT(String16)
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
* A StaticString16 object is a specialized String16 object. Instead of holding
|
||||
* the string data in a ref counted SharedBuffer object, it holds data in a
|
||||
* buffer within StaticString16 itself. Note that this buffer is NOT ref
|
||||
* counted and is assumed to be available for as long as there is at least a
|
||||
* String16 object using it. Therefore, one must be extra careful to NEVER
|
||||
* assign a StaticString16 to a String16 that outlives the StaticString16
|
||||
* object.
|
||||
*
|
||||
* THE SAFEST APPROACH IS TO USE StaticString16 ONLY AS GLOBAL VARIABLES.
|
||||
*
|
||||
* A StaticString16 SHOULD NEVER APPEAR IN APIs. USE String16 INSTEAD.
|
||||
*/
|
||||
template <size_t N>
|
||||
class StaticString16 : public String16 {
|
||||
public:
|
||||
constexpr StaticString16(const char16_t (&s)[N]) : String16(mData), mData(makeStaticData(s)) {}
|
||||
|
||||
constexpr StaticString16(const StaticString16<N>& other)
|
||||
: String16(mData), mData(other.mData) {}
|
||||
|
||||
constexpr StaticString16(const StaticString16<N>&&) = delete;
|
||||
|
||||
// There is no reason why one would want to 'new' a StaticString16. Delete
|
||||
// it to discourage misuse.
|
||||
static void* operator new(std::size_t) = delete;
|
||||
|
||||
private:
|
||||
const StaticData<N> mData;
|
||||
};
|
||||
|
||||
template <typename F>
|
||||
StaticString16(const F&)->StaticString16<sizeof(F) / sizeof(char16_t)>;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// No user servicable parts below.
|
||||
|
||||
|
|
Loading…
Reference in a new issue