blob: 2812a35235aed46b3f586bb427311664cdf49290 [file] [log] [blame]
// Copyright 2014 The Android Open Source Project
//
// This software is licensed under the terms of the GNU General Public
// License version 2, as published by the Free Software Foundation, and
// may be copied, distributed, and modified under those terms.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
#include "android/base/String.h"
#include "android/base/Limits.h"
#include "android/base/Log.h"
#include "android/base/memory/MallocUsableSize.h"
#include "android/base/StringView.h"
#include <limits.h>
#include <stdlib.h>
#include <string.h>
namespace android {
namespace base {
String::String() : mStr(mStorage), mSize(0) {
mStorage[0] = '\0';
}
String::String(const char* str) : mStr(mStorage), mSize(0) {
assign(str, ::strlen(str));
}
String::String(const char* str, size_t len) : mStr(mStorage), mSize(0) {
assign(str, len);
}
String::String(const String& other) : mStr(mStorage), mSize(0) {
assign(other);
}
String::String(const StringView& other) : mStr(mStorage), mSize(0) {
assign(other);
}
String::String(size_t count, char fill) : mStr(mStorage), mSize(0) {
this->assign(count, fill);
}
String::~String() {
reserve(0U);
// Prevent misuse from dangling pointers.
mStr = NULL;
mSize = 0;
mCapacity = 0;
}
String& String::assign(const char* str) {
return this->assign(str, ::strlen(str));
}
String& String::assign(const char* str, size_t len) {
this->resize(len);
::memmove(mStr, str, len);
return *this;
}
String& String::assign(const String& other) {
return this->assign(other.c_str(), other.size());
}
String& String::assign(const StringView& other) {
return this->assign(other.str(), other.size());
}
String& String::assign(char ch) {
return this->assign(&ch, 1U);
}
String& String::assign(size_t count, char fill) {
this->resize(count);
::memset(mStr, fill, count);
return *this;
}
String& String::append(const char* str, size_t len) {
size_t oldSize = mSize;
this->resize(mSize + len);
::memmove(mStr + oldSize, str, len);
return *this;
}
String& String::append(const char* str) {
return this->append(str, ::strlen(str));
}
String& String::append(const String& other) {
return this->append(other.c_str(), other.size());
}
String& String::append(const StringView& other) {
return this->append(other.str(), other.size());
}
String& String::append(char ch) {
return this->append(&ch, 1U);
}
int String::compare(const char* str, size_t len) const {
if (mSize == 0)
return (len == 0) ? 0 : -1;
if (len == 0)
return +1;
int ret = ::strncmp(mStr, str, len);
if (ret < 0)
return -1;
if (ret > 0)
return +1;
if (mSize < len)
return -1;
if (mSize > len)
return +1;
return 0;
}
int String::compare(const char* str) const {
return compare(str, ::strlen(str));
}
int String::compare(const String& other) const {
return compare(other.c_str(), other.size());
}
int String::compare(const StringView& other) const {
return compare(other.str(), other.size());
}
int String::compare(char ch) const {
return compare(&ch, 1U);
}
bool String::equals(const char* str, size_t len) const {
if (mSize == 0)
return (len == 0);
if (len != mSize)
return false;
return !::memcmp(mStr, str, len);
}
bool String::equals(const char* str) const {
return equals(str, ::strlen(str));
}
bool String::equals(const String& other) const {
return equals(other.c_str(), other.size());
}
bool String::equals(const StringView& other) const {
return equals(other.str(), other.size());
}
bool String::equals(char ch) const {
return equals(&ch, 1U);
}
void String::resize(size_t newSize) {
if (!mStr)
mStr = mStorage;
size_t oldCapacity = capacity();
if (newSize < oldCapacity) {
if (oldCapacity >= 256U && newSize < oldCapacity / 2) {
reserve(newSize);
}
} else if (newSize > oldCapacity) {
const size_t kMaxCapacity = SIZE_MAX - 1U;
CHECK(newSize < kMaxCapacity);
size_t newCapacity = oldCapacity;
while (newCapacity < newSize) {
size_t newCapacity2 = newCapacity + (newCapacity >> 2) + 8;
newCapacity = (newCapacity2 < newCapacity)
? kMaxCapacity : newCapacity2;
}
reserve(newCapacity);
}
DCHECK(newSize <= capacity());
mSize = newSize;
mStr[newSize] = '\0';
}
void String::reserve(size_t newSize) {
size_t minSize = (newSize < kMinCapacity) ? kMinCapacity : newSize;
if (!mStr)
mStr = mStorage;
if (minSize == kMinCapacity) {
if (mStr != mStorage) {
// Copy the first bytes to mStorage, then free the heap
// allocated buffer.
::memcpy(mStorage, mStr, newSize);
::free(mStr);
mStr = mStorage;
}
} else /* newSize > kMinCapacity */ {
char* oldStorage = (mStr == mStorage) ? NULL : mStr;
size_t newStorageSize = newSize + 1U;
mStr = static_cast<char*>(::realloc(oldStorage, newStorageSize));
#if xxxUSE_MALLOC_USABLE_SIZE
size_t usableSize = malloc_usable_size(mStr);
if (usableSize > newStorageSize)
newStorageSize = usableSize;
#endif
if (!oldStorage) {
::memcpy(mStr, mStorage, mSize);
}
if (newSize > mSize) {
::memset(mStr + mSize, 0, newSize - mSize);
}
mCapacity = newStorageSize - 1U;
}
mStr[newSize] = '\0';
}
void String::swap(String* other) {
if (this == other)
return;
char* myStr = mStr;
size_t mySize = mSize;
size_t myCapacity = capacity();
char* theirStr = other->mStr;
size_t theirSize = other->mSize;
size_t theirCapacity = other->capacity();
if (myStr == mStorage) {
if (theirStr == other->mStorage) {
// Two small strings, swap buffer contents, no need to swap
// pointers or capacities.
for (size_t n = 0; n < kMinCapacity + 1U; ++n) {
char tmp = myStr[n];
myStr[n] = theirStr[n];
theirStr[n] = tmp;
}
} else {
// |this| is a small string, |other| is a long one.
::memcpy(other->mStorage, mStorage, mySize + 1U);
other->mStr = other->mStorage;
mStr = theirStr;
mCapacity = theirCapacity;
}
} else if (theirStr == other->mStorage) {
// |this| is a long string, |other| is a short string.
::memcpy(mStorage, other->mStorage, theirSize + 1U);
other->mStr = myStr;
other->mCapacity = myCapacity;
mStr = mStorage;
} else {
// Both |this| and |other| are long strings.
mStr = theirStr;
mCapacity = theirCapacity;
other->mStr = myStr;
other->mCapacity = myCapacity;
}
// Always swap the sizes.
mSize = theirSize;
other->mSize = mySize;
}
// static
void String::adjustMovedSlice(String* fromStrings,
String* toStrings,
size_t count) {
for (size_t n = 0; n < count; ++n) {
if (toStrings[n].mStr == fromStrings[n].mStorage) {
toStrings[n].mStr = toStrings[n].mStorage;
}
}
}
// static
void String::moveSlice(String* strings,
size_t from,
size_t to,
size_t count) {
// First, move all slice items with ::memmove().
::memmove(strings + to, strings + from, count * sizeof(String));
// Second, adjust mStorage pointers.
adjustMovedSlice(strings + from, strings + to, count);
}
void String::finalizeSlice(String* strings, size_t count) {
for (size_t n = count; n > 0; --n) {
strings[n - 1U].reserve(0U);
}
}
} // namespace base
} // namespace android