blob: 23c85f993ec3cc37c361e06816c08e66d982ef93 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001/*
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
Hal Canaryc640d0d2018-06-13 09:59:02 -04008#include "SkString.h"
9
herbb906daf2015-09-29 09:37:59 -070010#include "SkAtomics.h"
Mike Reed33f38b02018-02-14 13:58:06 -050011#include "SkSafeMath.h"
Hal Canaryc640d0d2018-06-13 09:59:02 -040012#include "SkTo.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000013#include "SkUtils.h"
Hal Canaryc640d0d2018-06-13 09:59:02 -040014
Mike Klein79aea6a2018-06-11 10:45:26 -040015#include <cstdio>
16#include <new>
17#include <stdarg.h>
reed@google.comfa06e522011-02-28 21:29:58 +000018
19// number of bytes (on the stack) to receive the printf result
bsalomon@google.com77cf4602013-04-22 21:05:48 +000020static const size_t kBufferSize = 1024;
reed@google.comfa06e522011-02-28 21:29:58 +000021
Brian Salomon43227522018-04-25 12:58:23 -040022static const char* apply_format_string(const char* format, va_list args, char* stackBuffer,
23 size_t stackBufferSize, int* length, SkString* heapBuffer) {
24 va_list argsCopy;
25 va_copy(argsCopy, args);
26 *length = std::vsnprintf(stackBuffer, stackBufferSize, format, args);
27 if (*length < 0) {
28 SkDebugf("SkString: vsnprintf reported error.");
29 va_end(argsCopy);
30 *length = 0;
31 return stackBuffer;
32 }
33 if (*length < SkToInt(stackBufferSize)) {
34 va_end(argsCopy);
35 return stackBuffer;
36 }
37 heapBuffer->resize(*length);
38 SkDEBUGCODE(int check =)
39 std::vsnprintf(heapBuffer->writable_str(), *length + 1, format, argsCopy);
40 SkASSERT(check == *length);
41 va_end(argsCopy);
42 return heapBuffer->c_str();
43}
44
45#define ARGS_TO_BUFFER(format, buffer, size, written, result) \
46 SkString overflow; \
47 do { \
48 va_list args; \
49 va_start(args, format); \
50 result = apply_format_string(format, args, buffer, size, &written, &overflow); \
51 va_end(args); \
reed@google.comfa06e522011-02-28 21:29:58 +000052 } while (0)
53
Brian Salomon43227522018-04-25 12:58:23 -040054#define V_SKSTRING_PRINTF(output, format) \
55 do { \
56 char buffer[kBufferSize]; \
57 va_list args; \
58 va_start(args, format); \
59 int length; \
60 auto result = apply_format_string(format, args, buffer, kBufferSize, &length, &output); \
61 SkASSERT(result == output.c_str() || result == buffer); \
62 if (result == buffer) { \
63 output.set(buffer, length); \
64 } \
65 } while (0)
halcanaryd51bdae2016-04-25 09:25:35 -070066
reed@google.comfa06e522011-02-28 21:29:58 +000067///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +000068
epoger@google.come8ebeb12012-10-29 16:42:11 +000069bool SkStrEndsWith(const char string[], const char suffixStr[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000070 SkASSERT(string);
epoger@google.come8ebeb12012-10-29 16:42:11 +000071 SkASSERT(suffixStr);
reed@android.com8a1c16f2008-12-17 15:59:43 +000072 size_t strLen = strlen(string);
epoger@google.come8ebeb12012-10-29 16:42:11 +000073 size_t suffixLen = strlen(suffixStr);
reed@android.com8a1c16f2008-12-17 15:59:43 +000074 return strLen >= suffixLen &&
epoger@google.come8ebeb12012-10-29 16:42:11 +000075 !strncmp(string + strLen - suffixLen, suffixStr, suffixLen);
76}
77
78bool SkStrEndsWith(const char string[], const char suffixChar) {
79 SkASSERT(string);
80 size_t strLen = strlen(string);
81 if (0 == strLen) {
82 return false;
83 } else {
84 return (suffixChar == string[strLen-1]);
85 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000086}
87
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +000088int SkStrStartsWithOneOf(const char string[], const char prefixes[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000089 int index = 0;
90 do {
91 const char* limit = strchr(prefixes, '\0');
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +000092 if (!strncmp(string, prefixes, limit - prefixes)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000093 return index;
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +000094 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000095 prefixes = limit + 1;
96 index++;
97 } while (prefixes[0]);
98 return -1;
99}
100
epoger@google.comd88a3d82013-06-19 18:27:20 +0000101char* SkStrAppendU32(char string[], uint32_t dec) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000102 SkDEBUGCODE(char* start = string;)
103
epoger@google.comd88a3d82013-06-19 18:27:20 +0000104 char buffer[SkStrAppendU32_MaxSize];
reed@android.com8a1c16f2008-12-17 15:59:43 +0000105 char* p = buffer + sizeof(buffer);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000106
reed@android.com8a1c16f2008-12-17 15:59:43 +0000107 do {
108 *--p = SkToU8('0' + dec % 10);
109 dec /= 10;
110 } while (dec != 0);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000111
reed@android.com8a1c16f2008-12-17 15:59:43 +0000112 SkASSERT(p >= buffer);
113 char* stop = buffer + sizeof(buffer);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000114 while (p < stop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000115 *string++ = *p++;
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000116 }
epoger@google.comd88a3d82013-06-19 18:27:20 +0000117 SkASSERT(string - start <= SkStrAppendU32_MaxSize);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000118 return string;
119}
120
epoger@google.comd88a3d82013-06-19 18:27:20 +0000121char* SkStrAppendS32(char string[], int32_t dec) {
mtklein225fb982014-11-05 11:35:21 -0800122 uint32_t udec = dec;
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +0000123 if (dec < 0) {
epoger@google.comd88a3d82013-06-19 18:27:20 +0000124 *string++ = '-';
mtklein225fb982014-11-05 11:35:21 -0800125 udec = ~udec + 1; // udec = -udec, but silences some warnings that are trying to be helpful
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +0000126 }
mtklein225fb982014-11-05 11:35:21 -0800127 return SkStrAppendU32(string, udec);
epoger@google.comd88a3d82013-06-19 18:27:20 +0000128}
129
130char* SkStrAppendU64(char string[], uint64_t dec, int minDigits) {
131 SkDEBUGCODE(char* start = string;)
132
133 char buffer[SkStrAppendU64_MaxSize];
134 char* p = buffer + sizeof(buffer);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000135
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +0000136 do {
caryclark@google.com803eceb2012-06-06 12:09:34 +0000137 *--p = SkToU8('0' + (int32_t) (dec % 10));
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +0000138 dec /= 10;
139 minDigits--;
140 } while (dec != 0);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000141
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +0000142 while (minDigits > 0) {
143 *--p = '0';
144 minDigits--;
145 }
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +0000146
147 SkASSERT(p >= buffer);
148 size_t cp_len = buffer + sizeof(buffer) - p;
149 memcpy(string, p, cp_len);
150 string += cp_len;
151
epoger@google.comd88a3d82013-06-19 18:27:20 +0000152 SkASSERT(string - start <= SkStrAppendU64_MaxSize);
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +0000153 return string;
154}
155
epoger@google.comd88a3d82013-06-19 18:27:20 +0000156char* SkStrAppendS64(char string[], int64_t dec, int minDigits) {
mtklein225fb982014-11-05 11:35:21 -0800157 uint64_t udec = dec;
epoger@google.comd88a3d82013-06-19 18:27:20 +0000158 if (dec < 0) {
159 *string++ = '-';
mtklein225fb982014-11-05 11:35:21 -0800160 udec = ~udec + 1; // udec = -udec, but silences some warnings that are trying to be helpful
epoger@google.comd88a3d82013-06-19 18:27:20 +0000161 }
mtklein225fb982014-11-05 11:35:21 -0800162 return SkStrAppendU64(string, udec, minDigits);
epoger@google.comd88a3d82013-06-19 18:27:20 +0000163}
164
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000165char* SkStrAppendFloat(char string[], float value) {
reed@google.com8072e4f2011-03-01 15:44:08 +0000166 // since floats have at most 8 significant digits, we limit our %g to that.
167 static const char gFormat[] = "%.8g";
168 // make it 1 larger for the terminating 0
169 char buffer[SkStrAppendScalar_MaxSize + 1];
Hal Canary09f818d2018-02-22 15:12:46 -0500170 int len = snprintf(buffer, sizeof(buffer), gFormat, value);
reed@google.com8072e4f2011-03-01 15:44:08 +0000171 memcpy(string, buffer, len);
vandebo@chromium.org677cbed2011-03-03 18:20:12 +0000172 SkASSERT(len <= SkStrAppendScalar_MaxSize);
reed@google.com8072e4f2011-03-01 15:44:08 +0000173 return string + len;
vandebo@chromium.org677cbed2011-03-03 18:20:12 +0000174}
reed@android.com8a1c16f2008-12-17 15:59:43 +0000175
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000176///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000177
Ben Wagnereccda1c2017-10-05 10:13:51 -0400178const SkString::Rec SkString::gEmptyRec(0, 0);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000179
180#define SizeOfRec() (gEmptyRec.data() - (const char*)&gEmptyRec)
181
reed@google.com8cb10882013-06-04 20:36:52 +0000182static uint32_t trim_size_t_to_u32(size_t value) {
183 if (sizeof(size_t) > sizeof(uint32_t)) {
Hal Canarye1bc7de2018-06-13 15:01:39 -0400184 if (value > UINT32_MAX) {
185 value = UINT32_MAX;
reed@google.com8cb10882013-06-04 20:36:52 +0000186 }
187 }
188 return (uint32_t)value;
189}
190
191static size_t check_add32(size_t base, size_t extra) {
Hal Canarye1bc7de2018-06-13 15:01:39 -0400192 SkASSERT(base <= UINT32_MAX);
reed@google.com8cb10882013-06-04 20:36:52 +0000193 if (sizeof(size_t) > sizeof(uint32_t)) {
Hal Canarye1bc7de2018-06-13 15:01:39 -0400194 if (base + extra > UINT32_MAX) {
195 extra = UINT32_MAX - base;
reed@google.com8cb10882013-06-04 20:36:52 +0000196 }
197 }
198 return extra;
199}
200
Ben Wagnereccda1c2017-10-05 10:13:51 -0400201sk_sp<SkString::Rec> SkString::Rec::Make(const char text[], size_t len) {
reed@google.com4bce1152011-09-14 16:13:58 +0000202 if (0 == len) {
Ben Wagnereccda1c2017-10-05 10:13:51 -0400203 return sk_sp<SkString::Rec>(const_cast<Rec*>(&gEmptyRec));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000204 }
Ben Wagnereccda1c2017-10-05 10:13:51 -0400205
Mike Reed33f38b02018-02-14 13:58:06 -0500206 SkSafeMath safe;
207 // We store a 32bit version of the length
208 uint32_t stringLen = safe.castTo<uint32_t>(len);
209 // Add SizeOfRec() for our overhead and 1 for null-termination
210 size_t allocationSize = safe.add(len, SizeOfRec() + sizeof(char));
211 // Align up to a multiple of 4
212 allocationSize = safe.alignUp(allocationSize, 4);
Ben Wagnereccda1c2017-10-05 10:13:51 -0400213
Mike Reed33f38b02018-02-14 13:58:06 -0500214 SkASSERT_RELEASE(safe.ok());
215
216 void* storage = ::operator new (allocationSize);
217 sk_sp<Rec> rec(new (storage) Rec(stringLen, 1));
Ben Wagnereccda1c2017-10-05 10:13:51 -0400218 if (text) {
219 memcpy(rec->data(), text, len);
220 }
221 rec->data()[len] = 0;
Robert Phillips01f8e412017-10-05 11:28:20 +0000222 return rec;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000223}
224
Ben Wagnereccda1c2017-10-05 10:13:51 -0400225void SkString::Rec::ref() const {
226 if (this == &SkString::gEmptyRec) {
227 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000228 }
Ben Wagnereccda1c2017-10-05 10:13:51 -0400229 SkAssertResult(this->fRefCnt.fetch_add(+1, std::memory_order_relaxed));
230}
231
232void SkString::Rec::unref() const {
233 if (this == &SkString::gEmptyRec) {
234 return;
235 }
236 int32_t oldRefCnt = this->fRefCnt.fetch_add(-1, std::memory_order_acq_rel);
237 SkASSERT(oldRefCnt);
238 if (1 == oldRefCnt) {
239 delete this;
240 }
241}
242
243bool SkString::Rec::unique() const {
244 return fRefCnt.load(std::memory_order_acquire) == 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000245}
246
247#ifdef SK_DEBUG
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000248void SkString::validate() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000249 // make sure know one has written over our global
reed@google.com4bce1152011-09-14 16:13:58 +0000250 SkASSERT(0 == gEmptyRec.fLength);
Ben Wagneraf893662017-10-03 11:08:14 -0400251 SkASSERT(0 == gEmptyRec.fRefCnt.load(std::memory_order_relaxed));
reed@google.com4bce1152011-09-14 16:13:58 +0000252 SkASSERT(0 == gEmptyRec.data()[0]);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000253
Ben Wagnereccda1c2017-10-05 10:13:51 -0400254 if (fRec.get() != &gEmptyRec) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000255 SkASSERT(fRec->fLength > 0);
Ben Wagneraf893662017-10-03 11:08:14 -0400256 SkASSERT(fRec->fRefCnt.load(std::memory_order_relaxed) > 0);
reed@google.com4bce1152011-09-14 16:13:58 +0000257 SkASSERT(0 == fRec->data()[fRec->fLength]);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000258 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000259}
260#endif
261
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000262///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000263
264SkString::SkString() : fRec(const_cast<Rec*>(&gEmptyRec)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000265}
266
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000267SkString::SkString(size_t len) {
Ben Wagnereccda1c2017-10-05 10:13:51 -0400268 fRec = Rec::Make(nullptr, len);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000269}
270
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000271SkString::SkString(const char text[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000272 size_t len = text ? strlen(text) : 0;
273
Ben Wagnereccda1c2017-10-05 10:13:51 -0400274 fRec = Rec::Make(text, len);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000275}
276
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000277SkString::SkString(const char text[], size_t len) {
Ben Wagnereccda1c2017-10-05 10:13:51 -0400278 fRec = Rec::Make(text, len);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000279}
280
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000281SkString::SkString(const SkString& src) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000282 src.validate();
283
Ben Wagnereccda1c2017-10-05 10:13:51 -0400284 fRec = src.fRec;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000285}
286
bungeman9d552972016-02-07 18:42:54 -0800287SkString::SkString(SkString&& src) {
288 src.validate();
289
Ben Wagnereccda1c2017-10-05 10:13:51 -0400290 fRec = std::move(src.fRec);
291 src.fRec.reset(const_cast<Rec*>(&gEmptyRec));
bungeman9d552972016-02-07 18:42:54 -0800292}
293
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000294SkString::~SkString() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000295 this->validate();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000296}
297
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000298bool SkString::equals(const SkString& src) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000299 return fRec == src.fRec || this->equals(src.c_str(), src.size());
300}
301
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000302bool SkString::equals(const char text[]) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000303 return this->equals(text, text ? strlen(text) : 0);
304}
305
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000306bool SkString::equals(const char text[], size_t len) const {
halcanary96fcdcc2015-08-27 07:41:13 -0700307 SkASSERT(len == 0 || text != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000308
309 return fRec->fLength == len && !memcmp(fRec->data(), text, len);
310}
311
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000312SkString& SkString::operator=(const SkString& src) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000313 this->validate();
314
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000315 if (fRec != src.fRec) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000316 SkString tmp(src);
317 this->swap(tmp);
318 }
319 return *this;
320}
321
bungeman9d552972016-02-07 18:42:54 -0800322SkString& SkString::operator=(SkString&& src) {
323 this->validate();
324
325 if (fRec != src.fRec) {
326 this->swap(src);
327 }
328 return *this;
329}
330
bsalomon@google.comfc296292011-05-06 13:53:47 +0000331SkString& SkString::operator=(const char text[]) {
332 this->validate();
333
334 SkString tmp(text);
335 this->swap(tmp);
336
337 return *this;
338}
339
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000340void SkString::reset() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000341 this->validate();
Ben Wagnereccda1c2017-10-05 10:13:51 -0400342 fRec.reset(const_cast<Rec*>(&gEmptyRec));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000343}
344
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000345char* SkString::writable_str() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000346 this->validate();
347
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000348 if (fRec->fLength) {
Ben Wagneraf893662017-10-03 11:08:14 -0400349 if (!fRec->unique()) {
Ben Wagnereccda1c2017-10-05 10:13:51 -0400350 fRec = Rec::Make(fRec->data(), fRec->fLength);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000351 }
352 }
353 return fRec->data();
354}
355
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000356void SkString::set(const char text[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000357 this->set(text, text ? strlen(text) : 0);
358}
359
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000360void SkString::set(const char text[], size_t len) {
reed@google.com8cb10882013-06-04 20:36:52 +0000361 len = trim_size_t_to_u32(len);
Ben Wagneraf893662017-10-03 11:08:14 -0400362 bool unique = fRec->unique();
reed@google.com4bce1152011-09-14 16:13:58 +0000363 if (0 == len) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000364 this->reset();
Ben Wagneraf893662017-10-03 11:08:14 -0400365 } else if (unique && len <= fRec->fLength) {
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000366 // should we resize if len <<<< fLength, to save RAM? (e.g. len < (fLength>>1))?
reed@android.com8a1c16f2008-12-17 15:59:43 +0000367 // just use less of the buffer without allocating a smaller one
368 char* p = this->writable_str();
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000369 if (text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000370 memcpy(p, text, len);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000371 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000372 p[len] = 0;
reed@google.com8cb10882013-06-04 20:36:52 +0000373 fRec->fLength = SkToU32(len);
Ben Wagneraf893662017-10-03 11:08:14 -0400374 } else if (unique && (fRec->fLength >> 2) == (len >> 2)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000375 // we have spare room in the current allocation, so don't alloc a larger one
376 char* p = this->writable_str();
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000377 if (text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000378 memcpy(p, text, len);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000379 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000380 p[len] = 0;
reed@google.com8cb10882013-06-04 20:36:52 +0000381 fRec->fLength = SkToU32(len);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000382 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000383 SkString tmp(text, len);
384 this->swap(tmp);
385 }
386}
387
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000388void SkString::insert(size_t offset, const char text[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000389 this->insert(offset, text, text ? strlen(text) : 0);
390}
391
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000392void SkString::insert(size_t offset, const char text[], size_t len) {
393 if (len) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000394 size_t length = fRec->fLength;
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000395 if (offset > length) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000396 offset = length;
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000397 }
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +0000398
reed@google.com8cb10882013-06-04 20:36:52 +0000399 // Check if length + len exceeds 32bits, we trim len
400 len = check_add32(length, len);
401 if (0 == len) {
402 return;
403 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000404
405 /* If we're the only owner, and we have room in our allocation for the insert,
406 do it in place, rather than allocating a new buffer.
407
408 To know we have room, compare the allocated sizes
409 beforeAlloc = SkAlign4(length + 1)
410 afterAlloc = SkAligh4(length + 1 + len)
411 but SkAlign4(x) is (x + 3) >> 2 << 2
412 which is equivalent for testing to (length + 1 + 3) >> 2 == (length + 1 + 3 + len) >> 2
413 and we can then eliminate the +1+3 since that doesn't affec the answer
414 */
Ben Wagneraf893662017-10-03 11:08:14 -0400415 if (fRec->unique() && (length >> 2) == ((length + len) >> 2)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000416 char* dst = this->writable_str();
417
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000418 if (offset < length) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000419 memmove(dst + offset + len, dst + offset, length - offset);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000420 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000421 memcpy(dst + offset, text, len);
422
423 dst[length + len] = 0;
reed@google.com8cb10882013-06-04 20:36:52 +0000424 fRec->fLength = SkToU32(length + len);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000425 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000426 /* Seems we should use realloc here, since that is safe if it fails
427 (we have the original data), and might be faster than alloc/copy/free.
428 */
429 SkString tmp(fRec->fLength + len);
430 char* dst = tmp.writable_str();
431
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000432 if (offset > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000433 memcpy(dst, fRec->data(), offset);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000434 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000435 memcpy(dst + offset, text, len);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000436 if (offset < fRec->fLength) {
437 memcpy(dst + offset + len, fRec->data() + offset,
438 fRec->fLength - offset);
439 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000440
441 this->swap(tmp);
442 }
443 }
444}
445
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000446void SkString::insertUnichar(size_t offset, SkUnichar uni) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000447 char buffer[kMaxBytesInUTF8Sequence];
448 size_t len = SkUTF8_FromUnichar(uni, buffer);
449
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000450 if (len) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000451 this->insert(offset, buffer, len);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000452 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000453}
454
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000455void SkString::insertS32(size_t offset, int32_t dec) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000456 char buffer[SkStrAppendS32_MaxSize];
457 char* stop = SkStrAppendS32(buffer, dec);
458 this->insert(offset, buffer, stop - buffer);
459}
460
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000461void SkString::insertS64(size_t offset, int64_t dec, int minDigits) {
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +0000462 char buffer[SkStrAppendS64_MaxSize];
463 char* stop = SkStrAppendS64(buffer, dec, minDigits);
464 this->insert(offset, buffer, stop - buffer);
465}
466
epoger@google.comd88a3d82013-06-19 18:27:20 +0000467void SkString::insertU32(size_t offset, uint32_t dec) {
468 char buffer[SkStrAppendU32_MaxSize];
469 char* stop = SkStrAppendU32(buffer, dec);
470 this->insert(offset, buffer, stop - buffer);
471}
472
473void SkString::insertU64(size_t offset, uint64_t dec, int minDigits) {
474 char buffer[SkStrAppendU64_MaxSize];
475 char* stop = SkStrAppendU64(buffer, dec, minDigits);
476 this->insert(offset, buffer, stop - buffer);
477}
478
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000479void SkString::insertHex(size_t offset, uint32_t hex, int minDigits) {
bungeman62ce0302015-08-28 09:09:32 -0700480 minDigits = SkTPin(minDigits, 0, 8);
reed@google.comfa06e522011-02-28 21:29:58 +0000481
reed@android.com8a1c16f2008-12-17 15:59:43 +0000482 char buffer[8];
483 char* p = buffer + sizeof(buffer);
484
485 do {
Hal Canaryd6e6e662017-06-17 10:38:13 -0400486 *--p = SkHexadecimalDigits::gUpper[hex & 0xF];
reed@android.com8a1c16f2008-12-17 15:59:43 +0000487 hex >>= 4;
488 minDigits -= 1;
489 } while (hex != 0);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000490
491 while (--minDigits >= 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000492 *--p = '0';
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000493 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000494
495 SkASSERT(p >= buffer);
496 this->insert(offset, p, buffer + sizeof(buffer) - p);
497}
498
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000499void SkString::insertScalar(size_t offset, SkScalar value) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000500 char buffer[SkStrAppendScalar_MaxSize];
501 char* stop = SkStrAppendScalar(buffer, value);
502 this->insert(offset, buffer, stop - buffer);
503}
504
reed@android.com8a1c16f2008-12-17 15:59:43 +0000505void SkString::printf(const char format[], ...) {
halcanaryd51bdae2016-04-25 09:25:35 -0700506 V_SKSTRING_PRINTF((*this), format);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000507}
508
509void SkString::appendf(const char format[], ...) {
Brian Salomon43227522018-04-25 12:58:23 -0400510 char buffer[kBufferSize];
joshualitt976386b2014-10-23 18:23:32 -0700511 int length;
Brian Salomon43227522018-04-25 12:58:23 -0400512 const char* result;
513 ARGS_TO_BUFFER(format, buffer, kBufferSize, length, result);
reed@google.comfa06e522011-02-28 21:29:58 +0000514
Brian Salomon43227522018-04-25 12:58:23 -0400515 this->append(result, length);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000516}
517
commit-bot@chromium.orgce0e4ef2013-11-21 17:20:17 +0000518void SkString::appendVAList(const char format[], va_list args) {
Brian Salomon43227522018-04-25 12:58:23 -0400519 char buffer[kBufferSize];
Hal Canary09f818d2018-02-22 15:12:46 -0500520 int length = vsnprintf(buffer, kBufferSize, format, args);
joshualitt976386b2014-10-23 18:23:32 -0700521 SkASSERT(length >= 0 && length < SkToInt(kBufferSize));
bsalomon@google.comf910d3b2013-03-07 17:06:57 +0000522
joshualitt976386b2014-10-23 18:23:32 -0700523 this->append(buffer, length);
bsalomon@google.comf910d3b2013-03-07 17:06:57 +0000524}
525
reed@android.com8a1c16f2008-12-17 15:59:43 +0000526void SkString::prependf(const char format[], ...) {
Brian Salomon43227522018-04-25 12:58:23 -0400527 char buffer[kBufferSize];
joshualitt976386b2014-10-23 18:23:32 -0700528 int length;
Brian Salomon43227522018-04-25 12:58:23 -0400529 const char* result;
530 ARGS_TO_BUFFER(format, buffer, kBufferSize, length, result);
reed@google.comfa06e522011-02-28 21:29:58 +0000531
Brian Salomon43227522018-04-25 12:58:23 -0400532 this->prepend(result, length);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000533}
534
joshualitt30ba4362014-08-21 20:18:45 -0700535void SkString::prependVAList(const char format[], va_list args) {
Brian Salomon43227522018-04-25 12:58:23 -0400536 char buffer[kBufferSize];
Hal Canary09f818d2018-02-22 15:12:46 -0500537 int length = vsnprintf(buffer, kBufferSize, format, args);
joshualitt976386b2014-10-23 18:23:32 -0700538 SkASSERT(length >= 0 && length < SkToInt(kBufferSize));
joshualitt30ba4362014-08-21 20:18:45 -0700539
joshualitt976386b2014-10-23 18:23:32 -0700540 this->prepend(buffer, length);
joshualitt30ba4362014-08-21 20:18:45 -0700541}
542
543
reed@google.comfa06e522011-02-28 21:29:58 +0000544///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000545
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000546void SkString::remove(size_t offset, size_t length) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000547 size_t size = this->size();
548
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000549 if (offset < size) {
bungemandfb9bc42014-08-11 07:19:56 -0700550 if (length > size - offset) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000551 length = size - offset;
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000552 }
bungemandfb9bc42014-08-11 07:19:56 -0700553 SkASSERT(length <= size);
554 SkASSERT(offset <= size - length);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000555 if (length > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000556 SkString tmp(size - length);
557 char* dst = tmp.writable_str();
558 const char* src = this->c_str();
559
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000560 if (offset) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000561 memcpy(dst, src, offset);
562 }
bungemandfb9bc42014-08-11 07:19:56 -0700563 size_t tail = size - (offset + length);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000564 if (tail) {
bungemandfb9bc42014-08-11 07:19:56 -0700565 memcpy(dst + offset, src + (offset + length), tail);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000566 }
567 SkASSERT(dst[tmp.size()] == 0);
568 this->swap(tmp);
569 }
570 }
571}
572
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000573void SkString::swap(SkString& other) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000574 this->validate();
575 other.validate();
576
Ben Wagnereccda1c2017-10-05 10:13:51 -0400577 SkTSwap(fRec, other.fRec);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000578}
579
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000580///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000581
tomhudson@google.com3a1f6a02011-06-30 14:39:52 +0000582SkString SkStringPrintf(const char* format, ...) {
583 SkString formattedOutput;
halcanaryd51bdae2016-04-25 09:25:35 -0700584 V_SKSTRING_PRINTF(formattedOutput, format);
tomhudson@google.com3a1f6a02011-06-30 14:39:52 +0000585 return formattedOutput;
586}
587
kkinnunen3e980c32015-12-23 01:33:00 -0800588void SkStrSplit(const char* str, const char* delimiters, SkStrSplitMode splitMode,
589 SkTArray<SkString>* out) {
590 if (splitMode == kCoalesce_SkStrSplitMode) {
rmistry0f515bd2015-12-22 10:22:26 -0800591 // Skip any delimiters.
592 str += strspn(str, delimiters);
kkinnunen9ebc3f02015-12-21 23:48:13 -0800593 }
kkinnunen3e980c32015-12-23 01:33:00 -0800594 if (!*str) {
595 return;
596 }
597
598 while (true) {
599 // Find a token.
600 const size_t len = strcspn(str, delimiters);
601 if (splitMode == kStrict_SkStrSplitMode || len > 0) {
602 out->push_back().set(str, len);
603 str += len;
604 }
605
606 if (!*str) {
607 return;
608 }
609 if (splitMode == kCoalesce_SkStrSplitMode) {
610 // Skip any delimiters.
611 str += strspn(str, delimiters);
612 } else {
613 // Skip one delimiter.
614 str += 1;
615 }
616 }
rmistry@google.comd6bab022013-12-02 13:50:38 +0000617}