123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604 |
- /*
- * Copyright 2006 The Android Open Source Project
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
- #include "include/core/SkString.h"
- #include "include/private/SkTo.h"
- #include "src/core/SkSafeMath.h"
- #include "src/core/SkUtils.h"
- #include "src/utils/SkUTF.h"
- #include <cstdio>
- #include <new>
- #include <utility>
- #include <vector>
- // number of bytes (on the stack) to receive the printf result
- static const size_t kBufferSize = 1024;
- static const char* apply_format_string(const char* format, va_list args, char* stackBuffer,
- size_t stackBufferSize, int* length, SkString* heapBuffer) {
- va_list argsCopy;
- va_copy(argsCopy, args);
- *length = std::vsnprintf(stackBuffer, stackBufferSize, format, args);
- if (*length < 0) {
- SkDebugf("SkString: vsnprintf reported error.");
- va_end(argsCopy);
- *length = 0;
- return stackBuffer;
- }
- if (*length < SkToInt(stackBufferSize)) {
- va_end(argsCopy);
- return stackBuffer;
- }
- heapBuffer->resize(*length);
- SkDEBUGCODE(int check =)
- std::vsnprintf(heapBuffer->writable_str(), *length + 1, format, argsCopy);
- SkASSERT(check == *length);
- va_end(argsCopy);
- return heapBuffer->c_str();
- }
- #define ARGS_TO_BUFFER(format, buffer, size, written, result) \
- SkString overflow; \
- do { \
- va_list args; \
- va_start(args, format); \
- result = apply_format_string(format, args, buffer, size, &written, &overflow); \
- va_end(args); \
- } while (0)
- #define V_SKSTRING_PRINTF(output, format) \
- do { \
- char buffer[kBufferSize]; \
- va_list args; \
- va_start(args, format); \
- int length; \
- auto result = apply_format_string(format, args, buffer, kBufferSize, &length, &output); \
- SkASSERT(result == output.c_str() || result == buffer); \
- if (result == buffer) { \
- output.set(buffer, length); \
- } \
- } while (0)
- ///////////////////////////////////////////////////////////////////////////////
- bool SkStrEndsWith(const char string[], const char suffixStr[]) {
- SkASSERT(string);
- SkASSERT(suffixStr);
- size_t strLen = strlen(string);
- size_t suffixLen = strlen(suffixStr);
- return strLen >= suffixLen &&
- !strncmp(string + strLen - suffixLen, suffixStr, suffixLen);
- }
- bool SkStrEndsWith(const char string[], const char suffixChar) {
- SkASSERT(string);
- size_t strLen = strlen(string);
- if (0 == strLen) {
- return false;
- } else {
- return (suffixChar == string[strLen-1]);
- }
- }
- int SkStrStartsWithOneOf(const char string[], const char prefixes[]) {
- int index = 0;
- do {
- const char* limit = strchr(prefixes, '\0');
- if (!strncmp(string, prefixes, limit - prefixes)) {
- return index;
- }
- prefixes = limit + 1;
- index++;
- } while (prefixes[0]);
- return -1;
- }
- char* SkStrAppendU32(char string[], uint32_t dec) {
- SkDEBUGCODE(char* start = string;)
- char buffer[SkStrAppendU32_MaxSize];
- char* p = buffer + sizeof(buffer);
- do {
- *--p = SkToU8('0' + dec % 10);
- dec /= 10;
- } while (dec != 0);
- SkASSERT(p >= buffer);
- char* stop = buffer + sizeof(buffer);
- while (p < stop) {
- *string++ = *p++;
- }
- SkASSERT(string - start <= SkStrAppendU32_MaxSize);
- return string;
- }
- char* SkStrAppendS32(char string[], int32_t dec) {
- uint32_t udec = dec;
- if (dec < 0) {
- *string++ = '-';
- udec = ~udec + 1; // udec = -udec, but silences some warnings that are trying to be helpful
- }
- return SkStrAppendU32(string, udec);
- }
- char* SkStrAppendU64(char string[], uint64_t dec, int minDigits) {
- SkDEBUGCODE(char* start = string;)
- char buffer[SkStrAppendU64_MaxSize];
- char* p = buffer + sizeof(buffer);
- do {
- *--p = SkToU8('0' + (int32_t) (dec % 10));
- dec /= 10;
- minDigits--;
- } while (dec != 0);
- while (minDigits > 0) {
- *--p = '0';
- minDigits--;
- }
- SkASSERT(p >= buffer);
- size_t cp_len = buffer + sizeof(buffer) - p;
- memcpy(string, p, cp_len);
- string += cp_len;
- SkASSERT(string - start <= SkStrAppendU64_MaxSize);
- return string;
- }
- char* SkStrAppendS64(char string[], int64_t dec, int minDigits) {
- uint64_t udec = dec;
- if (dec < 0) {
- *string++ = '-';
- udec = ~udec + 1; // udec = -udec, but silences some warnings that are trying to be helpful
- }
- return SkStrAppendU64(string, udec, minDigits);
- }
- char* SkStrAppendFloat(char string[], float value) {
- // since floats have at most 8 significant digits, we limit our %g to that.
- static const char gFormat[] = "%.8g";
- // make it 1 larger for the terminating 0
- char buffer[SkStrAppendScalar_MaxSize + 1];
- int len = snprintf(buffer, sizeof(buffer), gFormat, value);
- memcpy(string, buffer, len);
- SkASSERT(len <= SkStrAppendScalar_MaxSize);
- return string + len;
- }
- ///////////////////////////////////////////////////////////////////////////////
- const SkString::Rec SkString::gEmptyRec(0, 0);
- #define SizeOfRec() (gEmptyRec.data() - (const char*)&gEmptyRec)
- static uint32_t trim_size_t_to_u32(size_t value) {
- if (sizeof(size_t) > sizeof(uint32_t)) {
- if (value > UINT32_MAX) {
- value = UINT32_MAX;
- }
- }
- return (uint32_t)value;
- }
- static size_t check_add32(size_t base, size_t extra) {
- SkASSERT(base <= UINT32_MAX);
- if (sizeof(size_t) > sizeof(uint32_t)) {
- if (base + extra > UINT32_MAX) {
- extra = UINT32_MAX - base;
- }
- }
- return extra;
- }
- sk_sp<SkString::Rec> SkString::Rec::Make(const char text[], size_t len) {
- if (0 == len) {
- return sk_sp<SkString::Rec>(const_cast<Rec*>(&gEmptyRec));
- }
- SkSafeMath safe;
- // We store a 32bit version of the length
- uint32_t stringLen = safe.castTo<uint32_t>(len);
- // Add SizeOfRec() for our overhead and 1 for null-termination
- size_t allocationSize = safe.add(len, SizeOfRec() + sizeof(char));
- // Align up to a multiple of 4
- allocationSize = safe.alignUp(allocationSize, 4);
- SkASSERT_RELEASE(safe.ok());
- void* storage = ::operator new (allocationSize);
- sk_sp<Rec> rec(new (storage) Rec(stringLen, 1));
- if (text) {
- memcpy(rec->data(), text, len);
- }
- rec->data()[len] = 0;
- return rec;
- }
- void SkString::Rec::ref() const {
- if (this == &SkString::gEmptyRec) {
- return;
- }
- SkAssertResult(this->fRefCnt.fetch_add(+1, std::memory_order_relaxed));
- }
- void SkString::Rec::unref() const {
- if (this == &SkString::gEmptyRec) {
- return;
- }
- int32_t oldRefCnt = this->fRefCnt.fetch_add(-1, std::memory_order_acq_rel);
- SkASSERT(oldRefCnt);
- if (1 == oldRefCnt) {
- delete this;
- }
- }
- bool SkString::Rec::unique() const {
- return fRefCnt.load(std::memory_order_acquire) == 1;
- }
- #ifdef SK_DEBUG
- const SkString& SkString::validate() const {
- // make sure know one has written over our global
- SkASSERT(0 == gEmptyRec.fLength);
- SkASSERT(0 == gEmptyRec.fRefCnt.load(std::memory_order_relaxed));
- SkASSERT(0 == gEmptyRec.data()[0]);
- if (fRec.get() != &gEmptyRec) {
- SkASSERT(fRec->fLength > 0);
- SkASSERT(fRec->fRefCnt.load(std::memory_order_relaxed) > 0);
- SkASSERT(0 == fRec->data()[fRec->fLength]);
- }
- return *this;
- }
- #endif
- ///////////////////////////////////////////////////////////////////////////////
- SkString::SkString() : fRec(const_cast<Rec*>(&gEmptyRec)) {
- }
- SkString::SkString(size_t len) {
- fRec = Rec::Make(nullptr, len);
- }
- SkString::SkString(const char text[]) {
- size_t len = text ? strlen(text) : 0;
- fRec = Rec::Make(text, len);
- }
- SkString::SkString(const char text[], size_t len) {
- fRec = Rec::Make(text, len);
- }
- SkString::SkString(const SkString& src) : fRec(src.validate().fRec) {}
- SkString::SkString(SkString&& src) : fRec(std::move(src.validate().fRec)) {
- src.fRec.reset(const_cast<Rec*>(&gEmptyRec));
- }
- SkString::~SkString() {
- this->validate();
- }
- bool SkString::equals(const SkString& src) const {
- return fRec == src.fRec || this->equals(src.c_str(), src.size());
- }
- bool SkString::equals(const char text[]) const {
- return this->equals(text, text ? strlen(text) : 0);
- }
- bool SkString::equals(const char text[], size_t len) const {
- SkASSERT(len == 0 || text != nullptr);
- return fRec->fLength == len && !memcmp(fRec->data(), text, len);
- }
- SkString& SkString::operator=(const SkString& src) {
- this->validate();
- fRec = src.fRec; // sk_sp<Rec>::operator=(const sk_sp<Ref>&) checks for self-assignment.
- return *this;
- }
- SkString& SkString::operator=(SkString&& src) {
- this->validate();
- if (fRec != src.fRec) {
- this->swap(src);
- }
- return *this;
- }
- SkString& SkString::operator=(const char text[]) {
- this->validate();
- return *this = SkString(text);
- }
- void SkString::reset() {
- this->validate();
- fRec.reset(const_cast<Rec*>(&gEmptyRec));
- }
- char* SkString::writable_str() {
- this->validate();
- if (fRec->fLength) {
- if (!fRec->unique()) {
- fRec = Rec::Make(fRec->data(), fRec->fLength);
- }
- }
- return fRec->data();
- }
- void SkString::set(const char text[]) {
- this->set(text, text ? strlen(text) : 0);
- }
- void SkString::set(const char text[], size_t len) {
- len = trim_size_t_to_u32(len);
- bool unique = fRec->unique();
- if (0 == len) {
- this->reset();
- } else if (unique && len <= fRec->fLength) {
- // should we resize if len <<<< fLength, to save RAM? (e.g. len < (fLength>>1))?
- // just use less of the buffer without allocating a smaller one
- char* p = this->writable_str();
- if (text) {
- memcpy(p, text, len);
- }
- p[len] = 0;
- fRec->fLength = SkToU32(len);
- } else if (unique && (fRec->fLength >> 2) == (len >> 2)) {
- // we have spare room in the current allocation, so don't alloc a larger one
- char* p = this->writable_str();
- if (text) {
- memcpy(p, text, len);
- }
- p[len] = 0;
- fRec->fLength = SkToU32(len);
- } else {
- SkString tmp(text, len);
- this->swap(tmp);
- }
- }
- void SkString::insert(size_t offset, const char text[]) {
- this->insert(offset, text, text ? strlen(text) : 0);
- }
- void SkString::insert(size_t offset, const char text[], size_t len) {
- if (len) {
- size_t length = fRec->fLength;
- if (offset > length) {
- offset = length;
- }
- // Check if length + len exceeds 32bits, we trim len
- len = check_add32(length, len);
- if (0 == len) {
- return;
- }
- /* If we're the only owner, and we have room in our allocation for the insert,
- do it in place, rather than allocating a new buffer.
- To know we have room, compare the allocated sizes
- beforeAlloc = SkAlign4(length + 1)
- afterAlloc = SkAligh4(length + 1 + len)
- but SkAlign4(x) is (x + 3) >> 2 << 2
- which is equivalent for testing to (length + 1 + 3) >> 2 == (length + 1 + 3 + len) >> 2
- and we can then eliminate the +1+3 since that doesn't affec the answer
- */
- if (fRec->unique() && (length >> 2) == ((length + len) >> 2)) {
- char* dst = this->writable_str();
- if (offset < length) {
- memmove(dst + offset + len, dst + offset, length - offset);
- }
- memcpy(dst + offset, text, len);
- dst[length + len] = 0;
- fRec->fLength = SkToU32(length + len);
- } else {
- /* Seems we should use realloc here, since that is safe if it fails
- (we have the original data), and might be faster than alloc/copy/free.
- */
- SkString tmp(fRec->fLength + len);
- char* dst = tmp.writable_str();
- if (offset > 0) {
- memcpy(dst, fRec->data(), offset);
- }
- memcpy(dst + offset, text, len);
- if (offset < fRec->fLength) {
- memcpy(dst + offset + len, fRec->data() + offset,
- fRec->fLength - offset);
- }
- this->swap(tmp);
- }
- }
- }
- void SkString::insertUnichar(size_t offset, SkUnichar uni) {
- char buffer[SkUTF::kMaxBytesInUTF8Sequence];
- size_t len = SkUTF::ToUTF8(uni, buffer);
- if (len) {
- this->insert(offset, buffer, len);
- }
- }
- void SkString::insertS32(size_t offset, int32_t dec) {
- char buffer[SkStrAppendS32_MaxSize];
- char* stop = SkStrAppendS32(buffer, dec);
- this->insert(offset, buffer, stop - buffer);
- }
- void SkString::insertS64(size_t offset, int64_t dec, int minDigits) {
- char buffer[SkStrAppendS64_MaxSize];
- char* stop = SkStrAppendS64(buffer, dec, minDigits);
- this->insert(offset, buffer, stop - buffer);
- }
- void SkString::insertU32(size_t offset, uint32_t dec) {
- char buffer[SkStrAppendU32_MaxSize];
- char* stop = SkStrAppendU32(buffer, dec);
- this->insert(offset, buffer, stop - buffer);
- }
- void SkString::insertU64(size_t offset, uint64_t dec, int minDigits) {
- char buffer[SkStrAppendU64_MaxSize];
- char* stop = SkStrAppendU64(buffer, dec, minDigits);
- this->insert(offset, buffer, stop - buffer);
- }
- void SkString::insertHex(size_t offset, uint32_t hex, int minDigits) {
- minDigits = SkTPin(minDigits, 0, 8);
- char buffer[8];
- char* p = buffer + sizeof(buffer);
- do {
- *--p = SkHexadecimalDigits::gUpper[hex & 0xF];
- hex >>= 4;
- minDigits -= 1;
- } while (hex != 0);
- while (--minDigits >= 0) {
- *--p = '0';
- }
- SkASSERT(p >= buffer);
- this->insert(offset, p, buffer + sizeof(buffer) - p);
- }
- void SkString::insertScalar(size_t offset, SkScalar value) {
- char buffer[SkStrAppendScalar_MaxSize];
- char* stop = SkStrAppendScalar(buffer, value);
- this->insert(offset, buffer, stop - buffer);
- }
- void SkString::printf(const char format[], ...) {
- V_SKSTRING_PRINTF((*this), format);
- }
- void SkString::appendf(const char format[], ...) {
- char buffer[kBufferSize];
- int length;
- const char* result;
- ARGS_TO_BUFFER(format, buffer, kBufferSize, length, result);
- this->append(result, length);
- }
- void SkString::appendVAList(const char format[], va_list args) {
- char buffer[kBufferSize];
- int length = vsnprintf(buffer, kBufferSize, format, args);
- SkASSERT(length >= 0 && length < SkToInt(kBufferSize));
- this->append(buffer, length);
- }
- void SkString::prependf(const char format[], ...) {
- char buffer[kBufferSize];
- int length;
- const char* result;
- ARGS_TO_BUFFER(format, buffer, kBufferSize, length, result);
- this->prepend(result, length);
- }
- void SkString::prependVAList(const char format[], va_list args) {
- char buffer[kBufferSize];
- int length = vsnprintf(buffer, kBufferSize, format, args);
- SkASSERT(length >= 0 && length < SkToInt(kBufferSize));
- this->prepend(buffer, length);
- }
- ///////////////////////////////////////////////////////////////////////////////
- void SkString::remove(size_t offset, size_t length) {
- size_t size = this->size();
- if (offset < size) {
- if (length > size - offset) {
- length = size - offset;
- }
- SkASSERT(length <= size);
- SkASSERT(offset <= size - length);
- if (length > 0) {
- SkString tmp(size - length);
- char* dst = tmp.writable_str();
- const char* src = this->c_str();
- if (offset) {
- memcpy(dst, src, offset);
- }
- size_t tail = size - (offset + length);
- if (tail) {
- memcpy(dst + offset, src + (offset + length), tail);
- }
- SkASSERT(dst[tmp.size()] == 0);
- this->swap(tmp);
- }
- }
- }
- void SkString::swap(SkString& other) {
- this->validate();
- other.validate();
- using std::swap;
- swap(fRec, other.fRec);
- }
- ///////////////////////////////////////////////////////////////////////////////
- SkString SkStringPrintf(const char* format, ...) {
- SkString formattedOutput;
- V_SKSTRING_PRINTF(formattedOutput, format);
- return formattedOutput;
- }
- void SkStrSplit(const char* str, const char* delimiters, SkStrSplitMode splitMode,
- SkTArray<SkString>* out) {
- if (splitMode == kCoalesce_SkStrSplitMode) {
- // Skip any delimiters.
- str += strspn(str, delimiters);
- }
- if (!*str) {
- return;
- }
- while (true) {
- // Find a token.
- const size_t len = strcspn(str, delimiters);
- if (splitMode == kStrict_SkStrSplitMode || len > 0) {
- out->push_back().set(str, len);
- str += len;
- }
- if (!*str) {
- return;
- }
- if (splitMode == kCoalesce_SkStrSplitMode) {
- // Skip any delimiters.
- str += strspn(str, delimiters);
- } else {
- // Skip one delimiter.
- str += 1;
- }
- }
- }
|