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#include "include/core/SkString.h"
9#include "include/private/SkTo.h"
10#include "src/core/SkSafeMath.h"
11#include "src/core/SkUtils.h"
12#include "src/utils/SkUTF.h"
13
14#include <cstdio>
15#include <new>
16#include <utility>
17#include <vector>
18
19// number of bytes (on the stack) to receive the printf result
20static const size_t kBufferSize = 1024;
21
22struct StringBuffer {
23 char* fText;
24 int fLength;
25};
26
27template <int SIZE>
28static StringBuffer apply_format_string(const char* format, va_list args, char (&stackBuffer)[SIZE],
29 SkString* heapBuffer) {
30 // First, attempt to print directly to the stack buffer.
31 va_list argsCopy;
32 va_copy(argsCopy, args);
33 int outLength = std::vsnprintf(stackBuffer, SIZE, format, args);
34 if (outLength < 0) {
35 SkDebugf("SkString: vsnprintf reported error.");
36 va_end(argsCopy);
37 return {stackBuffer, 0};
38 }
39 if (outLength < SIZE) {
40 va_end(argsCopy);
41 return {stackBuffer, outLength};
42 }
43
44 // Our text was too long to fit on the stack! However, we now know how much space we need to
45 // format it. Format the string into our heap buffer. `set` automatically reserves an extra
46 // byte at the end of the buffer for a null terminator, so we don't need to add one here.
47 heapBuffer->set(nullptr, outLength);
48 char* heapBufferDest = heapBuffer->writable_str();
49 SkDEBUGCODE(int checkLength =) std::vsnprintf(heapBufferDest, outLength + 1, format, argsCopy);
50 SkASSERT(checkLength == outLength);
51 va_end(argsCopy);
52 return {heapBufferDest, outLength};
53}
54
55///////////////////////////////////////////////////////////////////////////////
56
57bool SkStrEndsWith(const char string[], const char suffixStr[]) {
58 SkASSERT(string);
59 SkASSERT(suffixStr);
60 size_t strLen = strlen(string);
61 size_t suffixLen = strlen(suffixStr);
62 return strLen >= suffixLen &&
63 !strncmp(string + strLen - suffixLen, suffixStr, suffixLen);
64}
65
66bool SkStrEndsWith(const char string[], const char suffixChar) {
67 SkASSERT(string);
68 size_t strLen = strlen(string);
69 if (0 == strLen) {
70 return false;
71 } else {
72 return (suffixChar == string[strLen-1]);
73 }
74}
75
76int SkStrStartsWithOneOf(const char string[], const char prefixes[]) {
77 int index = 0;
78 do {
79 const char* limit = strchr(prefixes, '\0');
80 if (!strncmp(string, prefixes, limit - prefixes)) {
81 return index;
82 }
83 prefixes = limit + 1;
84 index++;
85 } while (prefixes[0]);
86 return -1;
87}
88
89char* SkStrAppendU32(char string[], uint32_t dec) {
90 SkDEBUGCODE(char* start = string;)
91
92 char buffer[kSkStrAppendU32_MaxSize];
93 char* p = buffer + sizeof(buffer);
94
95 do {
96 *--p = SkToU8('0' + dec % 10);
97 dec /= 10;
98 } while (dec != 0);
99
100 SkASSERT(p >= buffer);
101 char* stop = buffer + sizeof(buffer);
102 while (p < stop) {
103 *string++ = *p++;
104 }
105 SkASSERT(string - start <= kSkStrAppendU32_MaxSize);
106 return string;
107}
108
109char* SkStrAppendS32(char string[], int32_t dec) {
110 uint32_t udec = dec;
111 if (dec < 0) {
112 *string++ = '-';
113 udec = ~udec + 1; // udec = -udec, but silences some warnings that are trying to be helpful
114 }
115 return SkStrAppendU32(string, udec);
116}
117
118char* SkStrAppendU64(char string[], uint64_t dec, int minDigits) {
119 SkDEBUGCODE(char* start = string;)
120
121 char buffer[kSkStrAppendU64_MaxSize];
122 char* p = buffer + sizeof(buffer);
123
124 do {
125 *--p = SkToU8('0' + (int32_t) (dec % 10));
126 dec /= 10;
127 minDigits--;
128 } while (dec != 0);
129
130 while (minDigits > 0) {
131 *--p = '0';
132 minDigits--;
133 }
134
135 SkASSERT(p >= buffer);
136 size_t cp_len = buffer + sizeof(buffer) - p;
137 memcpy(string, p, cp_len);
138 string += cp_len;
139
140 SkASSERT(string - start <= kSkStrAppendU64_MaxSize);
141 return string;
142}
143
144char* SkStrAppendS64(char string[], int64_t dec, int minDigits) {
145 uint64_t udec = dec;
146 if (dec < 0) {
147 *string++ = '-';
148 udec = ~udec + 1; // udec = -udec, but silences some warnings that are trying to be helpful
149 }
150 return SkStrAppendU64(string, udec, minDigits);
151}
152
153char* SkStrAppendScalar(char string[], SkScalar value) {
154 // since floats have at most 8 significant digits, we limit our %g to that.
155 static const char gFormat[] = "%.8g";
156 // make it 1 larger for the terminating 0
157 char buffer[kSkStrAppendScalar_MaxSize + 1];
158 int len = snprintf(buffer, sizeof(buffer), gFormat, value);
159 memcpy(string, buffer, len);
160 SkASSERT(len <= kSkStrAppendScalar_MaxSize);
161 return string + len;
162}
163
164///////////////////////////////////////////////////////////////////////////////
165
166const SkString::Rec SkString::gEmptyRec(0, 0);
167
168#define SizeOfRec() (gEmptyRec.data() - (const char*)&gEmptyRec)
169
170static uint32_t trim_size_t_to_u32(size_t value) {
171 if (sizeof(size_t) > sizeof(uint32_t)) {
172 if (value > UINT32_MAX) {
173 value = UINT32_MAX;
174 }
175 }
176 return (uint32_t)value;
177}
178
179static size_t check_add32(size_t base, size_t extra) {
180 SkASSERT(base <= UINT32_MAX);
181 if (sizeof(size_t) > sizeof(uint32_t)) {
182 if (base + extra > UINT32_MAX) {
183 extra = UINT32_MAX - base;
184 }
185 }
186 return extra;
187}
188
189sk_sp<SkString::Rec> SkString::Rec::Make(const char text[], size_t len) {
190 if (0 == len) {
191 return sk_sp<SkString::Rec>(const_cast<Rec*>(&gEmptyRec));
192 }
193
194 SkSafeMath safe;
195 // We store a 32bit version of the length
196 uint32_t stringLen = safe.castTo<uint32_t>(len);
197 // Add SizeOfRec() for our overhead and 1 for null-termination
198 size_t allocationSize = safe.add(len, SizeOfRec() + sizeof(char));
199 // Align up to a multiple of 4
200 allocationSize = safe.alignUp(allocationSize, 4);
201
202 SkASSERT_RELEASE(safe.ok());
203
204 void* storage = ::operator new (allocationSize);
205 sk_sp<Rec> rec(new (storage) Rec(stringLen, 1));
206 if (text) {
207 memcpy(rec->data(), text, len);
208 }
209 rec->data()[len] = 0;
210 return rec;
211}
212
213void SkString::Rec::ref() const {
214 if (this == &SkString::gEmptyRec) {
215 return;
216 }
217 SkAssertResult(this->fRefCnt.fetch_add(+1, std::memory_order_relaxed));
218}
219
220void SkString::Rec::unref() const {
221 if (this == &SkString::gEmptyRec) {
222 return;
223 }
224 int32_t oldRefCnt = this->fRefCnt.fetch_add(-1, std::memory_order_acq_rel);
225 SkASSERT(oldRefCnt);
226 if (1 == oldRefCnt) {
227 delete this;
228 }
229}
230
231bool SkString::Rec::unique() const {
232 return fRefCnt.load(std::memory_order_acquire) == 1;
233}
234
235#ifdef SK_DEBUG
236const SkString& SkString::validate() const {
237 // make sure know one has written over our global
238 SkASSERT(0 == gEmptyRec.fLength);
239 SkASSERT(0 == gEmptyRec.fRefCnt.load(std::memory_order_relaxed));
240 SkASSERT(0 == gEmptyRec.data()[0]);
241
242 if (fRec.get() != &gEmptyRec) {
243 SkASSERT(fRec->fLength > 0);
244 SkASSERT(fRec->fRefCnt.load(std::memory_order_relaxed) > 0);
245 SkASSERT(0 == fRec->data()[fRec->fLength]);
246 }
247 return *this;
248}
249#endif
250
251///////////////////////////////////////////////////////////////////////////////
252
253SkString::SkString() : fRec(const_cast<Rec*>(&gEmptyRec)) {
254}
255
256SkString::SkString(size_t len) {
257 fRec = Rec::Make(nullptr, len);
258}
259
260SkString::SkString(const char text[]) {
261 size_t len = text ? strlen(text) : 0;
262
263 fRec = Rec::Make(text, len);
264}
265
266SkString::SkString(const char text[], size_t len) {
267 fRec = Rec::Make(text, len);
268}
269
270SkString::SkString(const SkString& src) : fRec(src.validate().fRec) {}
271
272SkString::SkString(SkString&& src) : fRec(std::move(src.validate().fRec)) {
273 src.fRec.reset(const_cast<Rec*>(&gEmptyRec));
274}
275
276SkString::SkString(const std::string& src) {
277 fRec = Rec::Make(src.c_str(), src.size());
278}
279
280SkString::~SkString() {
281 this->validate();
282}
283
284bool SkString::equals(const SkString& src) const {
285 return fRec == src.fRec || this->equals(src.c_str(), src.size());
286}
287
288bool SkString::equals(const char text[]) const {
289 return this->equals(text, text ? strlen(text) : 0);
290}
291
292bool SkString::equals(const char text[], size_t len) const {
293 SkASSERT(len == 0 || text != nullptr);
294
295 return fRec->fLength == len && !sk_careful_memcmp(fRec->data(), text, len);
296}
297
298SkString& SkString::operator=(const SkString& src) {
299 this->validate();
300 fRec = src.fRec; // sk_sp<Rec>::operator=(const sk_sp<Ref>&) checks for self-assignment.
301 return *this;
302}
303
304SkString& SkString::operator=(SkString&& src) {
305 this->validate();
306
307 if (fRec != src.fRec) {
308 this->swap(src);
309 }
310 return *this;
311}
312
313SkString& SkString::operator=(const char text[]) {
314 this->validate();
315 return *this = SkString(text);
316}
317
318void SkString::reset() {
319 this->validate();
320 fRec.reset(const_cast<Rec*>(&gEmptyRec));
321}
322
323char* SkString::writable_str() {
324 this->validate();
325
326 if (fRec->fLength) {
327 if (!fRec->unique()) {
328 fRec = Rec::Make(fRec->data(), fRec->fLength);
329 }
330 }
331 return fRec->data();
332}
333
334void SkString::resize(size_t len) {
335 len = trim_size_t_to_u32(len);
336 if (0 == len) {
337 this->reset();
338 } else if (fRec->unique() && ((len >> 2) <= (fRec->fLength >> 2))) {
339 // Use less of the buffer we have without allocating a smaller one.
340 char* p = this->writable_str();
341 p[len] = '\0';
342 fRec->fLength = SkToU32(len);
343 } else {
344 SkString newString(len);
345 char* dest = newString.writable_str();
346 int copyLen = std::min<uint32_t>(len, this->size());
347 memcpy(dest, this->c_str(), copyLen);
348 dest[copyLen] = '\0';
349 this->swap(newString);
350 }
351}
352
353void SkString::set(const char text[]) {
354 this->set(text, text ? strlen(text) : 0);
355}
356
357void SkString::set(const char text[], size_t len) {
358 len = trim_size_t_to_u32(len);
359 if (0 == len) {
360 this->reset();
361 } else if (fRec->unique() && ((len >> 2) <= (fRec->fLength >> 2))) {
362 // Use less of the buffer we have without allocating a smaller one.
363 char* p = this->writable_str();
364 if (text) {
365 memcpy(p, text, len);
366 }
367 p[len] = '\0';
368 fRec->fLength = SkToU32(len);
369 } else {
370 SkString tmp(text, len);
371 this->swap(tmp);
372 }
373}
374
375void SkString::insert(size_t offset, const char text[]) {
376 this->insert(offset, text, text ? strlen(text) : 0);
377}
378
379void SkString::insert(size_t offset, const char text[], size_t len) {
380 if (len) {
381 size_t length = fRec->fLength;
382 if (offset > length) {
383 offset = length;
384 }
385
386 // Check if length + len exceeds 32bits, we trim len
387 len = check_add32(length, len);
388 if (0 == len) {
389 return;
390 }
391
392 /* If we're the only owner, and we have room in our allocation for the insert,
393 do it in place, rather than allocating a new buffer.
394
395 To know we have room, compare the allocated sizes
396 beforeAlloc = SkAlign4(length + 1)
397 afterAlloc = SkAligh4(length + 1 + len)
398 but SkAlign4(x) is (x + 3) >> 2 << 2
399 which is equivalent for testing to (length + 1 + 3) >> 2 == (length + 1 + 3 + len) >> 2
400 and we can then eliminate the +1+3 since that doesn't affec the answer
401 */
402 if (fRec->unique() && (length >> 2) == ((length + len) >> 2)) {
403 char* dst = this->writable_str();
404
405 if (offset < length) {
406 memmove(dst + offset + len, dst + offset, length - offset);
407 }
408 memcpy(dst + offset, text, len);
409
410 dst[length + len] = 0;
411 fRec->fLength = SkToU32(length + len);
412 } else {
413 /* Seems we should use realloc here, since that is safe if it fails
414 (we have the original data), and might be faster than alloc/copy/free.
415 */
416 SkString tmp(fRec->fLength + len);
417 char* dst = tmp.writable_str();
418
419 if (offset > 0) {
420 memcpy(dst, fRec->data(), offset);
421 }
422 memcpy(dst + offset, text, len);
423 if (offset < fRec->fLength) {
424 memcpy(dst + offset + len, fRec->data() + offset,
425 fRec->fLength - offset);
426 }
427
428 this->swap(tmp);
429 }
430 }
431}
432
433void SkString::insertUnichar(size_t offset, SkUnichar uni) {
434 char buffer[SkUTF::kMaxBytesInUTF8Sequence];
435 size_t len = SkUTF::ToUTF8(uni, buffer);
436
437 if (len) {
438 this->insert(offset, buffer, len);
439 }
440}
441
442void SkString::insertS32(size_t offset, int32_t dec) {
443 char buffer[kSkStrAppendS32_MaxSize];
444 char* stop = SkStrAppendS32(buffer, dec);
445 this->insert(offset, buffer, stop - buffer);
446}
447
448void SkString::insertS64(size_t offset, int64_t dec, int minDigits) {
449 char buffer[kSkStrAppendS64_MaxSize];
450 char* stop = SkStrAppendS64(buffer, dec, minDigits);
451 this->insert(offset, buffer, stop - buffer);
452}
453
454void SkString::insertU32(size_t offset, uint32_t dec) {
455 char buffer[kSkStrAppendU32_MaxSize];
456 char* stop = SkStrAppendU32(buffer, dec);
457 this->insert(offset, buffer, stop - buffer);
458}
459
460void SkString::insertU64(size_t offset, uint64_t dec, int minDigits) {
461 char buffer[kSkStrAppendU64_MaxSize];
462 char* stop = SkStrAppendU64(buffer, dec, minDigits);
463 this->insert(offset, buffer, stop - buffer);
464}
465
466void SkString::insertHex(size_t offset, uint32_t hex, int minDigits) {
467 minDigits = SkTPin(minDigits, 0, 8);
468
469 char buffer[8];
470 char* p = buffer + sizeof(buffer);
471
472 do {
473 *--p = SkHexadecimalDigits::gUpper[hex & 0xF];
474 hex >>= 4;
475 minDigits -= 1;
476 } while (hex != 0);
477
478 while (--minDigits >= 0) {
479 *--p = '0';
480 }
481
482 SkASSERT(p >= buffer);
483 this->insert(offset, p, buffer + sizeof(buffer) - p);
484}
485
486void SkString::insertScalar(size_t offset, SkScalar value) {
487 char buffer[kSkStrAppendScalar_MaxSize];
488 char* stop = SkStrAppendScalar(buffer, value);
489 this->insert(offset, buffer, stop - buffer);
490}
491
492///////////////////////////////////////////////////////////////////////////////
493
494void SkString::printf(const char format[], ...) {
495 va_list args;
496 va_start(args, format);
497 this->printVAList(format, args);
498 va_end(args);
499}
500
501void SkString::printVAList(const char format[], va_list args) {
502 char stackBuffer[kBufferSize];
503 StringBuffer result = apply_format_string(format, args, stackBuffer, this);
504
505 if (result.fText == stackBuffer) {
506 this->set(result.fText, result.fLength);
507 }
508}
509
510void SkString::appendf(const char format[], ...) {
511 va_list args;
512 va_start(args, format);
513 this->appendVAList(format, args);
514 va_end(args);
515}
516
517void SkString::appendVAList(const char format[], va_list args) {
518 if (this->isEmpty()) {
519 this->printVAList(format, args);
520 return;
521 }
522
523 SkString overflow;
524 char stackBuffer[kBufferSize];
525 StringBuffer result = apply_format_string(format, args, stackBuffer, &overflow);
526
527 this->append(result.fText, result.fLength);
528}
529
530void SkString::prependf(const char format[], ...) {
531 va_list args;
532 va_start(args, format);
533 this->prependVAList(format, args);
534 va_end(args);
535}
536
537void SkString::prependVAList(const char format[], va_list args) {
538 if (this->isEmpty()) {
539 this->printVAList(format, args);
540 return;
541 }
542
543 SkString overflow;
544 char stackBuffer[kBufferSize];
545 StringBuffer result = apply_format_string(format, args, stackBuffer, &overflow);
546
547 this->prepend(result.fText, result.fLength);
548}
549
550///////////////////////////////////////////////////////////////////////////////
551
552void SkString::remove(size_t offset, size_t length) {
553 size_t size = this->size();
554
555 if (offset < size) {
556 if (length > size - offset) {
557 length = size - offset;
558 }
559 SkASSERT(length <= size);
560 SkASSERT(offset <= size - length);
561 if (length > 0) {
562 SkString tmp(size - length);
563 char* dst = tmp.writable_str();
564 const char* src = this->c_str();
565
566 if (offset) {
567 memcpy(dst, src, offset);
568 }
569 size_t tail = size - (offset + length);
570 if (tail) {
571 memcpy(dst + offset, src + (offset + length), tail);
572 }
573 SkASSERT(dst[tmp.size()] == 0);
574 this->swap(tmp);
575 }
576 }
577}
578
579void SkString::swap(SkString& other) {
580 this->validate();
581 other.validate();
582
583 using std::swap;
584 swap(fRec, other.fRec);
585}
586
587///////////////////////////////////////////////////////////////////////////////
588
589SkString SkStringPrintf(const char* format, ...) {
590 SkString formattedOutput;
591 va_list args;
592 va_start(args, format);
593 formattedOutput.printVAList(format, args);
594 va_end(args);
595 return formattedOutput;
596}
597
598void SkStrSplit(const char* str, const char* delimiters, SkStrSplitMode splitMode,
599 SkTArray<SkString>* out) {
600 if (splitMode == kCoalesce_SkStrSplitMode) {
601 // Skip any delimiters.
602 str += strspn(str, delimiters);
603 }
604 if (!*str) {
605 return;
606 }
607
608 while (true) {
609 // Find a token.
610 const size_t len = strcspn(str, delimiters);
611 if (splitMode == kStrict_SkStrSplitMode || len > 0) {
612 out->push_back().set(str, len);
613 str += len;
614 }
615
616 if (!*str) {
617 return;
618 }
619 if (splitMode == kCoalesce_SkStrSplitMode) {
620 // Skip any delimiters.
621 str += strspn(str, delimiters);
622 } else {
623 // Skip one delimiter.
624 str += 1;
625 }
626 }
627}
628