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 */
24static inline bool SkStrStartsWith(const char string[], const char prefixStr[]) {
25 SkASSERT(string);
26 SkASSERT(prefixStr);
27 return !strncmp(string, prefixStr, strlen(prefixStr));
28}
29static inline bool SkStrStartsWith(const char string[], const char prefixChar) {
30 SkASSERT(string);
31 return (prefixChar == *string);
32}
33
34bool SkStrEndsWith(const char string[], const char suffixStr[]);
35bool SkStrEndsWith(const char string[], const char suffixChar);
36
37int SkStrStartsWithOneOf(const char string[], const char prefixes[]);
38
39static 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
45static 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
51static inline bool SkStrContains(const char string[], const char substring[]) {
52 SkASSERT(string);
53 SkASSERT(substring);
54 return (-1 != SkStrFind(string, substring));
55}
56static 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
82static constexpr int kSkStrAppendU32_MaxSize = 10;
83char* SkStrAppendU32(char buffer[], uint32_t);
84static constexpr int kSkStrAppendU64_MaxSize = 20;
85char* SkStrAppendU64(char buffer[], uint64_t, int minDigits);
86
87static constexpr int kSkStrAppendS32_MaxSize = kSkStrAppendU32_MaxSize + 1;
88char* SkStrAppendS32(char buffer[], int32_t);
89static constexpr int kSkStrAppendS64_MaxSize = kSkStrAppendU64_MaxSize + 1;
90char* 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 */
99static 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 */
108char* 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*/
116class SK_API SkString {
117public:
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
236private:
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.
267SkString 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.
270static inline SkString SkStringPrintf() { return SkString(); }
271
272static inline void swap(SkString& a, SkString& b) {
273 a.swap(b);
274}
275
276enum 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.)
288void SkStrSplit(const char* str, const char* delimiters, SkStrSplitMode splitMode,
289 SkTArray<SkString>* out);
290inline void SkStrSplit(const char* str, const char* delimiters, SkTArray<SkString>* out) {
291 SkStrSplit(str, delimiters, kCoalesce_SkStrSplitMode, out);
292}
293
294#endif
295