| /* | |
| * Copyright (C) 2005 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. | |
| */ | |
| #define __STDC_LIMIT_MACROS | |
| #include <stdint.h> | |
| #include <utils/String8.h> | |
| #include <utils/Compat.h> | |
| #include <utils/Log.h> | |
| #include <utils/String16.h> | |
| #include <ctype.h> | |
| #include "SharedBuffer.h" | |
| /* | |
| * Functions outside android is below the namespace android, since they use | |
| * functions and constants in android namespace. | |
| */ | |
| // --------------------------------------------------------------------------- | |
| namespace android { | |
| // Separator used by resource paths. This is not platform dependent contrary | |
| // to OS_PATH_SEPARATOR. | |
| #define RES_PATH_SEPARATOR '/' | |
| static SharedBuffer* gEmptyStringBuf = NULL; | |
| static char* gEmptyString = NULL; | |
| extern int gDarwinCantLoadAllObjects; | |
| int gDarwinIsReallyAnnoying; | |
| void initialize_string8(); | |
| static inline char* getEmptyString() | |
| { | |
| gEmptyStringBuf->acquire(); | |
| return gEmptyString; | |
| } | |
| void initialize_string8() | |
| { | |
| // HACK: This dummy dependency forces linking libutils Static.cpp, | |
| // which is needed to initialize String8/String16 classes. | |
| // These variables are named for Darwin, but are needed elsewhere too, | |
| // including static linking on any platform. | |
| gDarwinIsReallyAnnoying = gDarwinCantLoadAllObjects; | |
| SharedBuffer* buf = SharedBuffer::alloc(1); | |
| char* str = (char*)buf->data(); | |
| *str = 0; | |
| gEmptyStringBuf = buf; | |
| gEmptyString = str; | |
| } | |
| void terminate_string8() | |
| { | |
| SharedBuffer::bufferFromData(gEmptyString)->release(); | |
| gEmptyStringBuf = NULL; | |
| gEmptyString = NULL; | |
| } | |
| // --------------------------------------------------------------------------- | |
| static char* allocFromUTF8(const char* in, size_t len) | |
| { | |
| if (len > 0) { | |
| if (len == SIZE_MAX) { | |
| return NULL; | |
| } | |
| SharedBuffer* buf = SharedBuffer::alloc(len+1); | |
| ALOG_ASSERT(buf, "Unable to allocate shared buffer"); | |
| if (buf) { | |
| char* str = (char*)buf->data(); | |
| memcpy(str, in, len); | |
| str[len] = 0; | |
| return str; | |
| } | |
| return NULL; | |
| } | |
| return getEmptyString(); | |
| } | |
| static char* allocFromUTF16(const char16_t* in, size_t len) | |
| { | |
| if (len == 0) return getEmptyString(); | |
| // Allow for closing '\0' | |
| const ssize_t resultStrLen = utf16_to_utf8_length(in, len) + 1; | |
| if (resultStrLen < 1) { | |
| return getEmptyString(); | |
| } | |
| SharedBuffer* buf = SharedBuffer::alloc(resultStrLen); | |
| ALOG_ASSERT(buf, "Unable to allocate shared buffer"); | |
| if (!buf) { | |
| return getEmptyString(); | |
| } | |
| char* resultStr = (char*)buf->data(); | |
| utf16_to_utf8(in, len, resultStr, resultStrLen); | |
| return resultStr; | |
| } | |
| static char* allocFromUTF32(const char32_t* in, size_t len) | |
| { | |
| if (len == 0) { | |
| return getEmptyString(); | |
| } | |
| const ssize_t resultStrLen = utf32_to_utf8_length(in, len) + 1; | |
| if (resultStrLen < 1) { | |
| return getEmptyString(); | |
| } | |
| SharedBuffer* buf = SharedBuffer::alloc(resultStrLen); | |
| ALOG_ASSERT(buf, "Unable to allocate shared buffer"); | |
| if (!buf) { | |
| return getEmptyString(); | |
| } | |
| char* resultStr = (char*) buf->data(); | |
| utf32_to_utf8(in, len, resultStr, resultStrLen); | |
| return resultStr; | |
| } | |
| // --------------------------------------------------------------------------- | |
| String8::String8() | |
| : mString(getEmptyString()) | |
| { | |
| } | |
| String8::String8(StaticLinkage) | |
| : mString(0) | |
| { | |
| // this constructor is used when we can't rely on the static-initializers | |
| // having run. In this case we always allocate an empty string. It's less | |
| // efficient than using getEmptyString(), but we assume it's uncommon. | |
| char* data = static_cast<char*>( | |
| SharedBuffer::alloc(sizeof(char))->data()); | |
| data[0] = 0; | |
| mString = data; | |
| } | |
| String8::String8(const String8& o) | |
| : mString(o.mString) | |
| { | |
| SharedBuffer::bufferFromData(mString)->acquire(); | |
| } | |
| String8::String8(const char* o) | |
| : mString(allocFromUTF8(o, strlen(o))) | |
| { | |
| if (mString == NULL) { | |
| mString = getEmptyString(); | |
| } | |
| } | |
| String8::String8(const char* o, size_t len) | |
| : mString(allocFromUTF8(o, len)) | |
| { | |
| if (mString == NULL) { | |
| mString = getEmptyString(); | |
| } | |
| } | |
| String8::String8(const String16& o) | |
| : mString(allocFromUTF16(o.string(), o.size())) | |
| { | |
| } | |
| String8::String8(const char16_t* o) | |
| : mString(allocFromUTF16(o, strlen16(o))) | |
| { | |
| } | |
| String8::String8(const char16_t* o, size_t len) | |
| : mString(allocFromUTF16(o, len)) | |
| { | |
| } | |
| String8::String8(const char32_t* o) | |
| : mString(allocFromUTF32(o, strlen32(o))) | |
| { | |
| } | |
| String8::String8(const char32_t* o, size_t len) | |
| : mString(allocFromUTF32(o, len)) | |
| { | |
| } | |
| String8::~String8() | |
| { | |
| SharedBuffer::bufferFromData(mString)->release(); | |
| } | |
| size_t String8::length() const | |
| { | |
| return SharedBuffer::sizeFromData(mString)-1; | |
| } | |
| String8 String8::format(const char* fmt, ...) | |
| { | |
| va_list args; | |
| va_start(args, fmt); | |
| String8 result(formatV(fmt, args)); | |
| va_end(args); | |
| return result; | |
| } | |
| String8 String8::formatV(const char* fmt, va_list args) | |
| { | |
| String8 result; | |
| result.appendFormatV(fmt, args); | |
| return result; | |
| } | |
| void String8::clear() { | |
| SharedBuffer::bufferFromData(mString)->release(); | |
| mString = getEmptyString(); | |
| } | |
| void String8::setTo(const String8& other) | |
| { | |
| SharedBuffer::bufferFromData(other.mString)->acquire(); | |
| SharedBuffer::bufferFromData(mString)->release(); | |
| mString = other.mString; | |
| } | |
| status_t String8::setTo(const char* other) | |
| { | |
| const char *newString = allocFromUTF8(other, strlen(other)); | |
| SharedBuffer::bufferFromData(mString)->release(); | |
| mString = newString; | |
| if (mString) return NO_ERROR; | |
| mString = getEmptyString(); | |
| return NO_MEMORY; | |
| } | |
| status_t String8::setTo(const char* other, size_t len) | |
| { | |
| const char *newString = allocFromUTF8(other, len); | |
| SharedBuffer::bufferFromData(mString)->release(); | |
| mString = newString; | |
| if (mString) return NO_ERROR; | |
| mString = getEmptyString(); | |
| return NO_MEMORY; | |
| } | |
| status_t String8::setTo(const char16_t* other, size_t len) | |
| { | |
| const char *newString = allocFromUTF16(other, len); | |
| SharedBuffer::bufferFromData(mString)->release(); | |
| mString = newString; | |
| if (mString) return NO_ERROR; | |
| mString = getEmptyString(); | |
| return NO_MEMORY; | |
| } | |
| status_t String8::setTo(const char32_t* other, size_t len) | |
| { | |
| const char *newString = allocFromUTF32(other, len); | |
| SharedBuffer::bufferFromData(mString)->release(); | |
| mString = newString; | |
| if (mString) return NO_ERROR; | |
| mString = getEmptyString(); | |
| return NO_MEMORY; | |
| } | |
| status_t String8::append(const String8& other) | |
| { | |
| const size_t otherLen = other.bytes(); | |
| if (bytes() == 0) { | |
| setTo(other); | |
| return NO_ERROR; | |
| } else if (otherLen == 0) { | |
| return NO_ERROR; | |
| } | |
| return real_append(other.string(), otherLen); | |
| } | |
| status_t String8::append(const char* other) | |
| { | |
| return append(other, strlen(other)); | |
| } | |
| status_t String8::append(const char* other, size_t otherLen) | |
| { | |
| if (bytes() == 0) { | |
| return setTo(other, otherLen); | |
| } else if (otherLen == 0) { | |
| return NO_ERROR; | |
| } | |
| return real_append(other, otherLen); | |
| } | |
| status_t String8::appendFormat(const char* fmt, ...) | |
| { | |
| va_list args; | |
| va_start(args, fmt); | |
| status_t result = appendFormatV(fmt, args); | |
| va_end(args); | |
| return result; | |
| } | |
| status_t String8::appendFormatV(const char* fmt, va_list args) | |
| { | |
| int n, result = NO_ERROR; | |
| va_list tmp_args; | |
| /* args is undefined after vsnprintf. | |
| * So we need a copy here to avoid the | |
| * second vsnprintf access undefined args. | |
| */ | |
| va_copy(tmp_args, args); | |
| n = vsnprintf(NULL, 0, fmt, tmp_args); | |
| va_end(tmp_args); | |
| if (n != 0) { | |
| size_t oldLength = length(); | |
| char* buf = lockBuffer(oldLength + n); | |
| if (buf) { | |
| vsnprintf(buf + oldLength, n + 1, fmt, args); | |
| } else { | |
| result = NO_MEMORY; | |
| } | |
| } | |
| return result; | |
| } | |
| status_t String8::real_append(const char* other, size_t otherLen) | |
| { | |
| const size_t myLen = bytes(); | |
| SharedBuffer* buf = SharedBuffer::bufferFromData(mString) | |
| ->editResize(myLen+otherLen+1); | |
| if (buf) { | |
| char* str = (char*)buf->data(); | |
| mString = str; | |
| str += myLen; | |
| memcpy(str, other, otherLen); | |
| str[otherLen] = '\0'; | |
| return NO_ERROR; | |
| } | |
| return NO_MEMORY; | |
| } | |
| char* String8::lockBuffer(size_t size) | |
| { | |
| SharedBuffer* buf = SharedBuffer::bufferFromData(mString) | |
| ->editResize(size+1); | |
| if (buf) { | |
| char* str = (char*)buf->data(); | |
| mString = str; | |
| return str; | |
| } | |
| return NULL; | |
| } | |
| void String8::unlockBuffer() | |
| { | |
| unlockBuffer(strlen(mString)); | |
| } | |
| status_t String8::unlockBuffer(size_t size) | |
| { | |
| if (size != this->size()) { | |
| SharedBuffer* buf = SharedBuffer::bufferFromData(mString) | |
| ->editResize(size+1); | |
| if (! buf) { | |
| return NO_MEMORY; | |
| } | |
| char* str = (char*)buf->data(); | |
| str[size] = 0; | |
| mString = str; | |
| } | |
| return NO_ERROR; | |
| } | |
| ssize_t String8::find(const char* other, size_t start) const | |
| { | |
| size_t len = size(); | |
| if (start >= len) { | |
| return -1; | |
| } | |
| const char* s = mString+start; | |
| const char* p = strstr(s, other); | |
| return p ? p-mString : -1; | |
| } | |
| bool String8::removeAll(const char* other) { | |
| ssize_t index = find(other); | |
| if (index < 0) return false; | |
| char* buf = lockBuffer(size()); | |
| if (!buf) return false; // out of memory | |
| size_t skip = strlen(other); | |
| size_t len = size(); | |
| size_t tail = index; | |
| while (size_t(index) < len) { | |
| ssize_t next = find(other, index + skip); | |
| if (next < 0) { | |
| next = len; | |
| } | |
| memmove(buf + tail, buf + index + skip, next - index - skip); | |
| tail += next - index - skip; | |
| index = next; | |
| } | |
| unlockBuffer(tail); | |
| return true; | |
| } | |
| void String8::toLower() | |
| { | |
| toLower(0, size()); | |
| } | |
| void String8::toLower(size_t start, size_t length) | |
| { | |
| const size_t len = size(); | |
| if (start >= len) { | |
| return; | |
| } | |
| if (start+length > len) { | |
| length = len-start; | |
| } | |
| char* buf = lockBuffer(len); | |
| buf += start; | |
| while (length > 0) { | |
| *buf = tolower(*buf); | |
| buf++; | |
| length--; | |
| } | |
| unlockBuffer(len); | |
| } | |
| void String8::toUpper() | |
| { | |
| toUpper(0, size()); | |
| } | |
| void String8::toUpper(size_t start, size_t length) | |
| { | |
| const size_t len = size(); | |
| if (start >= len) { | |
| return; | |
| } | |
| if (start+length > len) { | |
| length = len-start; | |
| } | |
| char* buf = lockBuffer(len); | |
| buf += start; | |
| while (length > 0) { | |
| *buf = toupper(*buf); | |
| buf++; | |
| length--; | |
| } | |
| unlockBuffer(len); | |
| } | |
| size_t String8::getUtf32Length() const | |
| { | |
| return utf8_to_utf32_length(mString, length()); | |
| } | |
| int32_t String8::getUtf32At(size_t index, size_t *next_index) const | |
| { | |
| return utf32_from_utf8_at(mString, length(), index, next_index); | |
| } | |
| void String8::getUtf32(char32_t* dst) const | |
| { | |
| utf8_to_utf32(mString, length(), dst); | |
| } | |
| // --------------------------------------------------------------------------- | |
| // Path functions | |
| void String8::setPathName(const char* name) | |
| { | |
| setPathName(name, strlen(name)); | |
| } | |
| void String8::setPathName(const char* name, size_t len) | |
| { | |
| char* buf = lockBuffer(len); | |
| memcpy(buf, name, len); | |
| // remove trailing path separator, if present | |
| if (len > 0 && buf[len-1] == OS_PATH_SEPARATOR) | |
| len--; | |
| buf[len] = '\0'; | |
| unlockBuffer(len); | |
| } | |
| String8 String8::getPathLeaf(void) const | |
| { | |
| const char* cp; | |
| const char*const buf = mString; | |
| cp = strrchr(buf, OS_PATH_SEPARATOR); | |
| if (cp == NULL) | |
| return String8(*this); | |
| else | |
| return String8(cp+1); | |
| } | |
| String8 String8::getPathDir(void) const | |
| { | |
| const char* cp; | |
| const char*const str = mString; | |
| cp = strrchr(str, OS_PATH_SEPARATOR); | |
| if (cp == NULL) | |
| return String8(""); | |
| else | |
| return String8(str, cp - str); | |
| } | |
| String8 String8::walkPath(String8* outRemains) const | |
| { | |
| const char* cp; | |
| const char*const str = mString; | |
| const char* buf = str; | |
| cp = strchr(buf, OS_PATH_SEPARATOR); | |
| if (cp == buf) { | |
| // don't include a leading '/'. | |
| buf = buf+1; | |
| cp = strchr(buf, OS_PATH_SEPARATOR); | |
| } | |
| if (cp == NULL) { | |
| String8 res = buf != str ? String8(buf) : *this; | |
| if (outRemains) *outRemains = String8(""); | |
| return res; | |
| } | |
| String8 res(buf, cp-buf); | |
| if (outRemains) *outRemains = String8(cp+1); | |
| return res; | |
| } | |
| /* | |
| * Helper function for finding the start of an extension in a pathname. | |
| * | |
| * Returns a pointer inside mString, or NULL if no extension was found. | |
| */ | |
| char* String8::find_extension(void) const | |
| { | |
| const char* lastSlash; | |
| const char* lastDot; | |
| const char* const str = mString; | |
| // only look at the filename | |
| lastSlash = strrchr(str, OS_PATH_SEPARATOR); | |
| if (lastSlash == NULL) | |
| lastSlash = str; | |
| else | |
| lastSlash++; | |
| // find the last dot | |
| lastDot = strrchr(lastSlash, '.'); | |
| if (lastDot == NULL) | |
| return NULL; | |
| // looks good, ship it | |
| return const_cast<char*>(lastDot); | |
| } | |
| String8 String8::getPathExtension(void) const | |
| { | |
| char* ext; | |
| ext = find_extension(); | |
| if (ext != NULL) | |
| return String8(ext); | |
| else | |
| return String8(""); | |
| } | |
| String8 String8::getBasePath(void) const | |
| { | |
| char* ext; | |
| const char* const str = mString; | |
| ext = find_extension(); | |
| if (ext == NULL) | |
| return String8(*this); | |
| else | |
| return String8(str, ext - str); | |
| } | |
| String8& String8::appendPath(const char* name) | |
| { | |
| // TODO: The test below will fail for Win32 paths. Fix later or ignore. | |
| if (name[0] != OS_PATH_SEPARATOR) { | |
| if (*name == '\0') { | |
| // nothing to do | |
| return *this; | |
| } | |
| size_t len = length(); | |
| if (len == 0) { | |
| // no existing filename, just use the new one | |
| setPathName(name); | |
| return *this; | |
| } | |
| // make room for oldPath + '/' + newPath | |
| int newlen = strlen(name); | |
| char* buf = lockBuffer(len+1+newlen); | |
| // insert a '/' if needed | |
| if (buf[len-1] != OS_PATH_SEPARATOR) | |
| buf[len++] = OS_PATH_SEPARATOR; | |
| memcpy(buf+len, name, newlen+1); | |
| len += newlen; | |
| unlockBuffer(len); | |
| return *this; | |
| } else { | |
| setPathName(name); | |
| return *this; | |
| } | |
| } | |
| String8& String8::convertToResPath() | |
| { | |
| #if OS_PATH_SEPARATOR != RES_PATH_SEPARATOR | |
| size_t len = length(); | |
| if (len > 0) { | |
| char * buf = lockBuffer(len); | |
| for (char * end = buf + len; buf < end; ++buf) { | |
| if (*buf == OS_PATH_SEPARATOR) | |
| *buf = RES_PATH_SEPARATOR; | |
| } | |
| unlockBuffer(len); | |
| } | |
| #endif | |
| return *this; | |
| } | |
| }; // namespace android |