| 1 | /* |
| 2 | * Copyright 2006 The Android Open Source Project |
| 3 | * |
| 4 | * Use of this source code is governed by a BSD-style license that can be |
| 5 | * found in the LICENSE file. |
| 6 | */ |
| 7 | |
| 8 | #ifndef SkString_DEFINED |
| 9 | #define SkString_DEFINED |
| 10 | |
| 11 | #include "include/core/SkRefCnt.h" |
| 12 | #include "include/core/SkScalar.h" |
| 13 | #include "include/core/SkTypes.h" |
| 14 | #include "include/private/SkMalloc.h" |
| 15 | #include "include/private/SkTArray.h" |
| 16 | #include "include/private/SkTo.h" |
| 17 | |
| 18 | #include <stdarg.h> |
| 19 | #include <string.h> |
| 20 | #include <atomic> |
| 21 | #include <string> |
| 22 | |
| 23 | /* Some helper functions for C strings */ |
| 24 | static inline bool SkStrStartsWith(const char string[], const char prefixStr[]) { |
| 25 | SkASSERT(string); |
| 26 | SkASSERT(prefixStr); |
| 27 | return !strncmp(string, prefixStr, strlen(prefixStr)); |
| 28 | } |
| 29 | static inline bool SkStrStartsWith(const char string[], const char prefixChar) { |
| 30 | SkASSERT(string); |
| 31 | return (prefixChar == *string); |
| 32 | } |
| 33 | |
| 34 | bool SkStrEndsWith(const char string[], const char suffixStr[]); |
| 35 | bool SkStrEndsWith(const char string[], const char suffixChar); |
| 36 | |
| 37 | int SkStrStartsWithOneOf(const char string[], const char prefixes[]); |
| 38 | |
| 39 | static inline int SkStrFind(const char string[], const char substring[]) { |
| 40 | const char *first = strstr(string, substring); |
| 41 | if (nullptr == first) return -1; |
| 42 | return SkToInt(first - &string[0]); |
| 43 | } |
| 44 | |
| 45 | static inline int SkStrFindLastOf(const char string[], const char subchar) { |
| 46 | const char* last = strrchr(string, subchar); |
| 47 | if (nullptr == last) return -1; |
| 48 | return SkToInt(last - &string[0]); |
| 49 | } |
| 50 | |
| 51 | static inline bool SkStrContains(const char string[], const char substring[]) { |
| 52 | SkASSERT(string); |
| 53 | SkASSERT(substring); |
| 54 | return (-1 != SkStrFind(string, substring)); |
| 55 | } |
| 56 | static inline bool SkStrContains(const char string[], const char subchar) { |
| 57 | SkASSERT(string); |
| 58 | char tmp[2]; |
| 59 | tmp[0] = subchar; |
| 60 | tmp[1] = '\0'; |
| 61 | return (-1 != SkStrFind(string, tmp)); |
| 62 | } |
| 63 | |
| 64 | /* |
| 65 | * The SkStrAppend... methods will write into the provided buffer, assuming it is large enough. |
| 66 | * Each method has an associated const (e.g. kSkStrAppendU32_MaxSize) which will be the largest |
| 67 | * value needed for that method's buffer. |
| 68 | * |
| 69 | * char storage[kSkStrAppendU32_MaxSize]; |
| 70 | * SkStrAppendU32(storage, value); |
| 71 | * |
| 72 | * Note : none of the SkStrAppend... methods write a terminating 0 to their buffers. Instead, |
| 73 | * the methods return the ptr to the end of the written part of the buffer. This can be used |
| 74 | * to compute the length, and/or know where to write a 0 if that is desired. |
| 75 | * |
| 76 | * char storage[kSkStrAppendU32_MaxSize + 1]; |
| 77 | * char* stop = SkStrAppendU32(storage, value); |
| 78 | * size_t len = stop - storage; |
| 79 | * *stop = 0; // valid, since storage was 1 byte larger than the max. |
| 80 | */ |
| 81 | |
| 82 | static constexpr int kSkStrAppendU32_MaxSize = 10; |
| 83 | char* SkStrAppendU32(char buffer[], uint32_t); |
| 84 | static constexpr int kSkStrAppendU64_MaxSize = 20; |
| 85 | char* SkStrAppendU64(char buffer[], uint64_t, int minDigits); |
| 86 | |
| 87 | static constexpr int kSkStrAppendS32_MaxSize = kSkStrAppendU32_MaxSize + 1; |
| 88 | char* SkStrAppendS32(char buffer[], int32_t); |
| 89 | static constexpr int kSkStrAppendS64_MaxSize = kSkStrAppendU64_MaxSize + 1; |
| 90 | char* SkStrAppendS64(char buffer[], int64_t, int minDigits); |
| 91 | |
| 92 | /** |
| 93 | * Floats have at most 8 significant digits, so we limit our %g to that. |
| 94 | * However, the total string could be 15 characters: -1.2345678e-005 |
| 95 | * |
| 96 | * In theory we should only expect up to 2 digits for the exponent, but on |
| 97 | * some platforms we have seen 3 (as in the example above). |
| 98 | */ |
| 99 | static constexpr int kSkStrAppendScalar_MaxSize = 15; |
| 100 | |
| 101 | /** |
| 102 | * Write the scalar in decimal format into buffer, and return a pointer to |
| 103 | * the next char after the last one written. Note: a terminating 0 is not |
| 104 | * written into buffer, which must be at least kSkStrAppendScalar_MaxSize. |
| 105 | * Thus if the caller wants to add a 0 at the end, buffer must be at least |
| 106 | * kSkStrAppendScalar_MaxSize + 1 bytes large. |
| 107 | */ |
| 108 | char* SkStrAppendScalar(char buffer[], SkScalar); |
| 109 | |
| 110 | /** \class SkString |
| 111 | |
| 112 | Light weight class for managing strings. Uses reference |
| 113 | counting to make string assignments and copies very fast |
| 114 | with no extra RAM cost. Assumes UTF8 encoding. |
| 115 | */ |
| 116 | class SK_API SkString { |
| 117 | public: |
| 118 | SkString(); |
| 119 | explicit SkString(size_t len); |
| 120 | explicit SkString(const char text[]); |
| 121 | SkString(const char text[], size_t len); |
| 122 | SkString(const SkString&); |
| 123 | SkString(SkString&&); |
| 124 | explicit SkString(const std::string&); |
| 125 | ~SkString(); |
| 126 | |
| 127 | bool isEmpty() const { return 0 == fRec->fLength; } |
| 128 | size_t size() const { return (size_t) fRec->fLength; } |
| 129 | const char* c_str() const { return fRec->data(); } |
| 130 | char operator[](size_t n) const { return this->c_str()[n]; } |
| 131 | |
| 132 | bool equals(const SkString&) const; |
| 133 | bool equals(const char text[]) const; |
| 134 | bool equals(const char text[], size_t len) const; |
| 135 | |
| 136 | bool startsWith(const char prefixStr[]) const { |
| 137 | return SkStrStartsWith(fRec->data(), prefixStr); |
| 138 | } |
| 139 | bool startsWith(const char prefixChar) const { |
| 140 | return SkStrStartsWith(fRec->data(), prefixChar); |
| 141 | } |
| 142 | bool endsWith(const char suffixStr[]) const { |
| 143 | return SkStrEndsWith(fRec->data(), suffixStr); |
| 144 | } |
| 145 | bool endsWith(const char suffixChar) const { |
| 146 | return SkStrEndsWith(fRec->data(), suffixChar); |
| 147 | } |
| 148 | bool contains(const char substring[]) const { |
| 149 | return SkStrContains(fRec->data(), substring); |
| 150 | } |
| 151 | bool contains(const char subchar) const { |
| 152 | return SkStrContains(fRec->data(), subchar); |
| 153 | } |
| 154 | int find(const char substring[]) const { |
| 155 | return SkStrFind(fRec->data(), substring); |
| 156 | } |
| 157 | int findLastOf(const char subchar) const { |
| 158 | return SkStrFindLastOf(fRec->data(), subchar); |
| 159 | } |
| 160 | |
| 161 | friend bool operator==(const SkString& a, const SkString& b) { |
| 162 | return a.equals(b); |
| 163 | } |
| 164 | friend bool operator!=(const SkString& a, const SkString& b) { |
| 165 | return !a.equals(b); |
| 166 | } |
| 167 | |
| 168 | // these methods edit the string |
| 169 | |
| 170 | SkString& operator=(const SkString&); |
| 171 | SkString& operator=(SkString&&); |
| 172 | SkString& operator=(const char text[]); |
| 173 | |
| 174 | char* writable_str(); |
| 175 | char& operator[](size_t n) { return this->writable_str()[n]; } |
| 176 | |
| 177 | void reset(); |
| 178 | /** String contents are preserved on resize. (For destructive resize, `set(nullptr, length)`.) |
| 179 | * `resize` automatically reserves an extra byte at the end of the buffer for a null terminator. |
| 180 | */ |
| 181 | void resize(size_t len); |
| 182 | void set(const SkString& src) { *this = src; } |
| 183 | void set(const char text[]); |
| 184 | void set(const char text[], size_t len); |
| 185 | |
| 186 | void insert(size_t offset, const SkString& src) { this->insert(offset, src.c_str(), src.size()); } |
| 187 | void insert(size_t offset, const char text[]); |
| 188 | void insert(size_t offset, const char text[], size_t len); |
| 189 | void insertUnichar(size_t offset, SkUnichar); |
| 190 | void insertS32(size_t offset, int32_t value); |
| 191 | void insertS64(size_t offset, int64_t value, int minDigits = 0); |
| 192 | void insertU32(size_t offset, uint32_t value); |
| 193 | void insertU64(size_t offset, uint64_t value, int minDigits = 0); |
| 194 | void insertHex(size_t offset, uint32_t value, int minDigits = 0); |
| 195 | void insertScalar(size_t offset, SkScalar); |
| 196 | |
| 197 | void append(const SkString& str) { this->insert((size_t)-1, str); } |
| 198 | void append(const char text[]) { this->insert((size_t)-1, text); } |
| 199 | void append(const char text[], size_t len) { this->insert((size_t)-1, text, len); } |
| 200 | void appendUnichar(SkUnichar uni) { this->insertUnichar((size_t)-1, uni); } |
| 201 | void appendS32(int32_t value) { this->insertS32((size_t)-1, value); } |
| 202 | void appendS64(int64_t value, int minDigits = 0) { this->insertS64((size_t)-1, value, minDigits); } |
| 203 | void appendU32(uint32_t value) { this->insertU32((size_t)-1, value); } |
| 204 | void appendU64(uint64_t value, int minDigits = 0) { this->insertU64((size_t)-1, value, minDigits); } |
| 205 | void appendHex(uint32_t value, int minDigits = 0) { this->insertHex((size_t)-1, value, minDigits); } |
| 206 | void appendScalar(SkScalar value) { this->insertScalar((size_t)-1, value); } |
| 207 | |
| 208 | void prepend(const SkString& str) { this->insert(0, str); } |
| 209 | void prepend(const char text[]) { this->insert(0, text); } |
| 210 | void prepend(const char text[], size_t len) { this->insert(0, text, len); } |
| 211 | void prependUnichar(SkUnichar uni) { this->insertUnichar(0, uni); } |
| 212 | void prependS32(int32_t value) { this->insertS32(0, value); } |
| 213 | void prependS64(int32_t value, int minDigits = 0) { this->insertS64(0, value, minDigits); } |
| 214 | void prependHex(uint32_t value, int minDigits = 0) { this->insertHex(0, value, minDigits); } |
| 215 | void prependScalar(SkScalar value) { this->insertScalar((size_t)-1, value); } |
| 216 | |
| 217 | void printf(const char format[], ...) SK_PRINTF_LIKE(2, 3); |
| 218 | void printVAList(const char format[], va_list); |
| 219 | void appendf(const char format[], ...) SK_PRINTF_LIKE(2, 3); |
| 220 | void appendVAList(const char format[], va_list); |
| 221 | void prependf(const char format[], ...) SK_PRINTF_LIKE(2, 3); |
| 222 | void prependVAList(const char format[], va_list); |
| 223 | |
| 224 | void remove(size_t offset, size_t length); |
| 225 | |
| 226 | SkString& operator+=(const SkString& s) { this->append(s); return *this; } |
| 227 | SkString& operator+=(const char text[]) { this->append(text); return *this; } |
| 228 | SkString& operator+=(const char c) { this->append(&c, 1); return *this; } |
| 229 | |
| 230 | /** |
| 231 | * Swap contents between this and other. This function is guaranteed |
| 232 | * to never fail or throw. |
| 233 | */ |
| 234 | void swap(SkString& other); |
| 235 | |
| 236 | private: |
| 237 | struct Rec { |
| 238 | public: |
| 239 | constexpr Rec(uint32_t len, int32_t refCnt) : fLength(len), fRefCnt(refCnt) {} |
| 240 | static sk_sp<Rec> Make(const char text[], size_t len); |
| 241 | char* data() { return &fBeginningOfData; } |
| 242 | const char* data() const { return &fBeginningOfData; } |
| 243 | void ref() const; |
| 244 | void unref() const; |
| 245 | bool unique() const; |
| 246 | |
| 247 | uint32_t fLength; // logically size_t, but we want it to stay 32 bits |
| 248 | mutable std::atomic<int32_t> fRefCnt; |
| 249 | char fBeginningOfData = '\0'; |
| 250 | |
| 251 | private: |
| 252 | // Ensure the unsized delete is called. |
| 253 | void operator delete(void* p) { ::operator delete(p); } |
| 254 | }; |
| 255 | sk_sp<Rec> fRec; |
| 256 | |
| 257 | #ifdef SK_DEBUG |
| 258 | const SkString& validate() const; |
| 259 | #else |
| 260 | const SkString& validate() const { return *this; } |
| 261 | #endif |
| 262 | |
| 263 | static const Rec gEmptyRec; |
| 264 | }; |
| 265 | |
| 266 | /// Creates a new string and writes into it using a printf()-style format. |
| 267 | SkString SkStringPrintf(const char* format, ...) SK_PRINTF_LIKE(1, 2); |
| 268 | /// This makes it easier to write a caller as a VAR_ARGS function where the format string is |
| 269 | /// optional. |
| 270 | static inline SkString SkStringPrintf() { return SkString(); } |
| 271 | |
| 272 | static inline void swap(SkString& a, SkString& b) { |
| 273 | a.swap(b); |
| 274 | } |
| 275 | |
| 276 | enum SkStrSplitMode { |
| 277 | // Strictly return all results. If the input is ",," and the separator is ',' this will return |
| 278 | // an array of three empty strings. |
| 279 | kStrict_SkStrSplitMode, |
| 280 | |
| 281 | // Only nonempty results will be added to the results. Multiple separators will be |
| 282 | // coalesced. Separators at the beginning and end of the input will be ignored. If the input is |
| 283 | // ",," and the separator is ',', this will return an empty vector. |
| 284 | kCoalesce_SkStrSplitMode |
| 285 | }; |
| 286 | |
| 287 | // Split str on any characters in delimiters into out. (Think, strtok with a sane API.) |
| 288 | void SkStrSplit(const char* str, const char* delimiters, SkStrSplitMode splitMode, |
| 289 | SkTArray<SkString>* out); |
| 290 | inline void SkStrSplit(const char* str, const char* delimiters, SkTArray<SkString>* out) { |
| 291 | SkStrSplit(str, delimiters, kCoalesce_SkStrSplitMode, out); |
| 292 | } |
| 293 | |
| 294 | #endif |
| 295 | |