| /* | |
| * 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. | |
| */ | |
| #ifndef SkString_DEFINED | |
| #define SkString_DEFINED | |
| #include "include/core/SkRefCnt.h" | |
| #include "include/core/SkScalar.h" | |
| #include "include/core/SkTypes.h" | |
| #include "include/private/base/SkTo.h" | |
| #include "include/private/base/SkTypeTraits.h" | |
| #include <atomic> | |
| #include <cstdarg> | |
| #include <cstdint> | |
| #include <cstring> | |
| #include <string> | |
| #include <string_view> | |
| #include <type_traits> | |
| /* Some helper functions for C strings */ | |
| static inline bool SkStrStartsWith(const char string[], const char prefixStr[]) { | |
| SkASSERT(string); | |
| SkASSERT(prefixStr); | |
| return !strncmp(string, prefixStr, strlen(prefixStr)); | |
| } | |
| static inline bool SkStrStartsWith(const char string[], const char prefixChar) { | |
| SkASSERT(string); | |
| return (prefixChar == *string); | |
| } | |
| bool SkStrEndsWith(const char string[], const char suffixStr[]); | |
| bool SkStrEndsWith(const char string[], const char suffixChar); | |
| int SkStrStartsWithOneOf(const char string[], const char prefixes[]); | |
| static inline int SkStrFind(const char string[], const char substring[]) { | |
| const char *first = strstr(string, substring); | |
| if (nullptr == first) return -1; | |
| return SkToInt(first - &string[0]); | |
| } | |
| static inline int SkStrFindLastOf(const char string[], const char subchar) { | |
| const char* last = strrchr(string, subchar); | |
| if (nullptr == last) return -1; | |
| return SkToInt(last - &string[0]); | |
| } | |
| static inline bool SkStrContains(const char string[], const char substring[]) { | |
| SkASSERT(string); | |
| SkASSERT(substring); | |
| return (-1 != SkStrFind(string, substring)); | |
| } | |
| static inline bool SkStrContains(const char string[], const char subchar) { | |
| SkASSERT(string); | |
| char tmp[2]; | |
| tmp[0] = subchar; | |
| tmp[1] = '\0'; | |
| return (-1 != SkStrFind(string, tmp)); | |
| } | |
| /* | |
| * The SkStrAppend... methods will write into the provided buffer, assuming it is large enough. | |
| * Each method has an associated const (e.g. kSkStrAppendU32_MaxSize) which will be the largest | |
| * value needed for that method's buffer. | |
| * | |
| * char storage[kSkStrAppendU32_MaxSize]; | |
| * SkStrAppendU32(storage, value); | |
| * | |
| * Note : none of the SkStrAppend... methods write a terminating 0 to their buffers. Instead, | |
| * the methods return the ptr to the end of the written part of the buffer. This can be used | |
| * to compute the length, and/or know where to write a 0 if that is desired. | |
| * | |
| * char storage[kSkStrAppendU32_MaxSize + 1]; | |
| * char* stop = SkStrAppendU32(storage, value); | |
| * size_t len = stop - storage; | |
| * *stop = 0; // valid, since storage was 1 byte larger than the max. | |
| */ | |
| static constexpr int kSkStrAppendU32_MaxSize = 10; | |
| char* SkStrAppendU32(char buffer[], uint32_t); | |
| static constexpr int kSkStrAppendU64_MaxSize = 20; | |
| char* SkStrAppendU64(char buffer[], uint64_t, int minDigits); | |
| static constexpr int kSkStrAppendS32_MaxSize = kSkStrAppendU32_MaxSize + 1; | |
| char* SkStrAppendS32(char buffer[], int32_t); | |
| static constexpr int kSkStrAppendS64_MaxSize = kSkStrAppendU64_MaxSize + 1; | |
| char* SkStrAppendS64(char buffer[], int64_t, int minDigits); | |
| /** | |
| * Floats have at most 8 significant digits, so we limit our %g to that. | |
| * However, the total string could be 15 characters: -1.2345678e-005 | |
| * | |
| * In theory we should only expect up to 2 digits for the exponent, but on | |
| * some platforms we have seen 3 (as in the example above). | |
| */ | |
| static constexpr int kSkStrAppendScalar_MaxSize = 15; | |
| /** | |
| * Write the scalar in decimal format into buffer, and return a pointer to | |
| * the next char after the last one written. Note: a terminating 0 is not | |
| * written into buffer, which must be at least kSkStrAppendScalar_MaxSize. | |
| * Thus if the caller wants to add a 0 at the end, buffer must be at least | |
| * kSkStrAppendScalar_MaxSize + 1 bytes large. | |
| */ | |
| char* SkStrAppendScalar(char buffer[], SkScalar); | |
| /** \class SkString | |
| Light weight class for managing strings. Uses reference | |
| counting to make string assignments and copies very fast | |
| with no extra RAM cost. Assumes UTF8 encoding. | |
| */ | |
| class SK_API SkString { | |
| public: | |
| SkString(); | |
| explicit SkString(size_t len); | |
| explicit SkString(const char text[]); | |
| SkString(const char text[], size_t len); | |
| SkString(const SkString&); | |
| SkString(SkString&&); | |
| explicit SkString(const std::string&); | |
| explicit SkString(std::string_view); | |
| ~SkString(); | |
| bool isEmpty() const { return 0 == fRec->fLength; } | |
| size_t size() const { return (size_t) fRec->fLength; } | |
| const char* data() const { return fRec->data(); } | |
| const char* c_str() const { return fRec->data(); } | |
| char operator[](size_t n) const { return this->c_str()[n]; } | |
| bool equals(const SkString&) const; | |
| bool equals(const char text[]) const; | |
| bool equals(const char text[], size_t len) const; | |
| bool startsWith(const char prefixStr[]) const { | |
| return SkStrStartsWith(fRec->data(), prefixStr); | |
| } | |
| bool startsWith(const char prefixChar) const { | |
| return SkStrStartsWith(fRec->data(), prefixChar); | |
| } | |
| bool endsWith(const char suffixStr[]) const { | |
| return SkStrEndsWith(fRec->data(), suffixStr); | |
| } | |
| bool endsWith(const char suffixChar) const { | |
| return SkStrEndsWith(fRec->data(), suffixChar); | |
| } | |
| bool contains(const char substring[]) const { | |
| return SkStrContains(fRec->data(), substring); | |
| } | |
| bool contains(const char subchar) const { | |
| return SkStrContains(fRec->data(), subchar); | |
| } | |
| int find(const char substring[]) const { | |
| return SkStrFind(fRec->data(), substring); | |
| } | |
| int findLastOf(const char subchar) const { | |
| return SkStrFindLastOf(fRec->data(), subchar); | |
| } | |
| friend bool operator==(const SkString& a, const SkString& b) { | |
| return a.equals(b); | |
| } | |
| friend bool operator!=(const SkString& a, const SkString& b) { | |
| return !a.equals(b); | |
| } | |
| // these methods edit the string | |
| SkString& operator=(const SkString&); | |
| SkString& operator=(SkString&&); | |
| SkString& operator=(const char text[]); | |
| char* data(); | |
| char& operator[](size_t n) { return this->data()[n]; } | |
| void reset(); | |
| /** String contents are preserved on resize. (For destructive resize, `set(nullptr, length)`.) | |
| * `resize` automatically reserves an extra byte at the end of the buffer for a null terminator. | |
| */ | |
| void resize(size_t len); | |
| void set(const SkString& src) { *this = src; } | |
| void set(const char text[]); | |
| void set(const char text[], size_t len); | |
| void set(std::string_view str) { this->set(str.data(), str.size()); } | |
| void insert(size_t offset, const char text[]); | |
| void insert(size_t offset, const char text[], size_t len); | |
| void insert(size_t offset, const SkString& str) { this->insert(offset, str.c_str(), str.size()); } | |
| void insert(size_t offset, std::string_view str) { this->insert(offset, str.data(), str.size()); } | |
| void insertUnichar(size_t offset, SkUnichar); | |
| void insertS32(size_t offset, int32_t value); | |
| void insertS64(size_t offset, int64_t value, int minDigits = 0); | |
| void insertU32(size_t offset, uint32_t value); | |
| void insertU64(size_t offset, uint64_t value, int minDigits = 0); | |
| void insertHex(size_t offset, uint32_t value, int minDigits = 0); | |
| void insertScalar(size_t offset, SkScalar); | |
| void append(const char text[]) { this->insert((size_t)-1, text); } | |
| void append(const char text[], size_t len) { this->insert((size_t)-1, text, len); } | |
| void append(const SkString& str) { this->insert((size_t)-1, str.c_str(), str.size()); } | |
| void append(std::string_view str) { this->insert((size_t)-1, str.data(), str.size()); } | |
| void appendUnichar(SkUnichar uni) { this->insertUnichar((size_t)-1, uni); } | |
| void appendS32(int32_t value) { this->insertS32((size_t)-1, value); } | |
| void appendS64(int64_t value, int minDigits = 0) { this->insertS64((size_t)-1, value, minDigits); } | |
| void appendU32(uint32_t value) { this->insertU32((size_t)-1, value); } | |
| void appendU64(uint64_t value, int minDigits = 0) { this->insertU64((size_t)-1, value, minDigits); } | |
| void appendHex(uint32_t value, int minDigits = 0) { this->insertHex((size_t)-1, value, minDigits); } | |
| void appendScalar(SkScalar value) { this->insertScalar((size_t)-1, value); } | |
| void prepend(const char text[]) { this->insert(0, text); } | |
| void prepend(const char text[], size_t len) { this->insert(0, text, len); } | |
| void prepend(const SkString& str) { this->insert(0, str.c_str(), str.size()); } | |
| void prepend(std::string_view str) { this->insert(0, str.data(), str.size()); } | |
| void prependUnichar(SkUnichar uni) { this->insertUnichar(0, uni); } | |
| void prependS32(int32_t value) { this->insertS32(0, value); } | |
| void prependS64(int32_t value, int minDigits = 0) { this->insertS64(0, value, minDigits); } | |
| void prependHex(uint32_t value, int minDigits = 0) { this->insertHex(0, value, minDigits); } | |
| void prependScalar(SkScalar value) { this->insertScalar((size_t)-1, value); } | |
| void printf(const char format[], ...) SK_PRINTF_LIKE(2, 3); | |
| void printVAList(const char format[], va_list) SK_PRINTF_LIKE(2, 0); | |
| void appendf(const char format[], ...) SK_PRINTF_LIKE(2, 3); | |
| void appendVAList(const char format[], va_list) SK_PRINTF_LIKE(2, 0); | |
| void prependf(const char format[], ...) SK_PRINTF_LIKE(2, 3); | |
| void prependVAList(const char format[], va_list) SK_PRINTF_LIKE(2, 0); | |
| void remove(size_t offset, size_t length); | |
| SkString& operator+=(const SkString& s) { this->append(s); return *this; } | |
| SkString& operator+=(const char text[]) { this->append(text); return *this; } | |
| SkString& operator+=(const char c) { this->append(&c, 1); return *this; } | |
| /** | |
| * Swap contents between this and other. This function is guaranteed | |
| * to never fail or throw. | |
| */ | |
| void swap(SkString& other); | |
| using sk_is_trivially_relocatable = std::true_type; | |
| private: | |
| struct Rec { | |
| public: | |
| constexpr Rec(uint32_t len, int32_t refCnt) : fLength(len), fRefCnt(refCnt) {} | |
| static sk_sp<Rec> Make(const char text[], size_t len); | |
| char* data() { return fBeginningOfData; } | |
| const char* data() const { return fBeginningOfData; } | |
| void ref() const; | |
| void unref() const; | |
| bool unique() const; | |
| #ifdef SK_DEBUG | |
| int32_t getRefCnt() const; | |
| #endif | |
| uint32_t fLength; // logically size_t, but we want it to stay 32 bits | |
| private: | |
| mutable std::atomic<int32_t> fRefCnt; | |
| char fBeginningOfData[1] = {'\0'}; | |
| // Ensure the unsized delete is called. | |
| void operator delete(void* p) { ::operator delete(p); } | |
| }; | |
| sk_sp<Rec> fRec; | |
| static_assert(::sk_is_trivially_relocatable<decltype(fRec)>::value); | |
| #ifdef SK_DEBUG | |
| SkString& validate(); | |
| const SkString& validate() const; | |
| #else | |
| SkString& validate() { return *this; } | |
| const SkString& validate() const { return *this; } | |
| #endif | |
| static const Rec gEmptyRec; | |
| }; | |
| /// Creates a new string and writes into it using a printf()-style format. | |
| SkString SkStringPrintf(const char* format, ...) SK_PRINTF_LIKE(1, 2); | |
| /// This makes it easier to write a caller as a VAR_ARGS function where the format string is | |
| /// optional. | |
| static inline SkString SkStringPrintf() { return SkString(); } | |
| static inline void swap(SkString& a, SkString& b) { | |
| a.swap(b); | |
| } | |
| #endif |