blob: 707e8befb8e45d90f90abd19f66cc0d1c37e4b90 [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
Brian Salomon43227522018-04-25 12:58:23 -04008#include "SkString.h"
9#include <stdarg.h>
10#include <cstdio>
herbb906daf2015-09-29 09:37:59 -070011#include "SkAtomics.h"
Mike Reed33f38b02018-02-14 13:58:06 -050012#include "SkSafeMath.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000013#include "SkUtils.h"
reed@google.comfa06e522011-02-28 21:29:58 +000014
15// number of bytes (on the stack) to receive the printf result
bsalomon@google.com77cf4602013-04-22 21:05:48 +000016static const size_t kBufferSize = 1024;
reed@google.comfa06e522011-02-28 21:29:58 +000017
Brian Salomon43227522018-04-25 12:58:23 -040018static const char* apply_format_string(const char* format, va_list args, char* stackBuffer,
19 size_t stackBufferSize, int* length, SkString* heapBuffer) {
20 va_list argsCopy;
21 va_copy(argsCopy, args);
22 *length = std::vsnprintf(stackBuffer, stackBufferSize, format, args);
23 if (*length < 0) {
24 SkDebugf("SkString: vsnprintf reported error.");
25 va_end(argsCopy);
26 *length = 0;
27 return stackBuffer;
28 }
29 if (*length < SkToInt(stackBufferSize)) {
30 va_end(argsCopy);
31 return stackBuffer;
32 }
33 heapBuffer->resize(*length);
34 SkDEBUGCODE(int check =)
35 std::vsnprintf(heapBuffer->writable_str(), *length + 1, format, argsCopy);
36 SkASSERT(check == *length);
37 va_end(argsCopy);
38 return heapBuffer->c_str();
39}
40
41#define ARGS_TO_BUFFER(format, buffer, size, written, result) \
42 SkString overflow; \
43 do { \
44 va_list args; \
45 va_start(args, format); \
46 result = apply_format_string(format, args, buffer, size, &written, &overflow); \
47 va_end(args); \
reed@google.comfa06e522011-02-28 21:29:58 +000048 } while (0)
49
Brian Salomon43227522018-04-25 12:58:23 -040050#define V_SKSTRING_PRINTF(output, format) \
51 do { \
52 char buffer[kBufferSize]; \
53 va_list args; \
54 va_start(args, format); \
55 int length; \
56 auto result = apply_format_string(format, args, buffer, kBufferSize, &length, &output); \
57 SkASSERT(result == output.c_str() || result == buffer); \
58 if (result == buffer) { \
59 output.set(buffer, length); \
60 } \
61 } while (0)
halcanaryd51bdae2016-04-25 09:25:35 -070062
reed@google.comfa06e522011-02-28 21:29:58 +000063///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +000064
epoger@google.come8ebeb12012-10-29 16:42:11 +000065bool SkStrEndsWith(const char string[], const char suffixStr[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000066 SkASSERT(string);
epoger@google.come8ebeb12012-10-29 16:42:11 +000067 SkASSERT(suffixStr);
reed@android.com8a1c16f2008-12-17 15:59:43 +000068 size_t strLen = strlen(string);
epoger@google.come8ebeb12012-10-29 16:42:11 +000069 size_t suffixLen = strlen(suffixStr);
reed@android.com8a1c16f2008-12-17 15:59:43 +000070 return strLen >= suffixLen &&
epoger@google.come8ebeb12012-10-29 16:42:11 +000071 !strncmp(string + strLen - suffixLen, suffixStr, suffixLen);
72}
73
74bool SkStrEndsWith(const char string[], const char suffixChar) {
75 SkASSERT(string);
76 size_t strLen = strlen(string);
77 if (0 == strLen) {
78 return false;
79 } else {
80 return (suffixChar == string[strLen-1]);
81 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000082}
83
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +000084int SkStrStartsWithOneOf(const char string[], const char prefixes[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000085 int index = 0;
86 do {
87 const char* limit = strchr(prefixes, '\0');
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +000088 if (!strncmp(string, prefixes, limit - prefixes)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000089 return index;
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +000090 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000091 prefixes = limit + 1;
92 index++;
93 } while (prefixes[0]);
94 return -1;
95}
96
epoger@google.comd88a3d82013-06-19 18:27:20 +000097char* SkStrAppendU32(char string[], uint32_t dec) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000098 SkDEBUGCODE(char* start = string;)
99
epoger@google.comd88a3d82013-06-19 18:27:20 +0000100 char buffer[SkStrAppendU32_MaxSize];
reed@android.com8a1c16f2008-12-17 15:59:43 +0000101 char* p = buffer + sizeof(buffer);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000102
reed@android.com8a1c16f2008-12-17 15:59:43 +0000103 do {
104 *--p = SkToU8('0' + dec % 10);
105 dec /= 10;
106 } while (dec != 0);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000107
reed@android.com8a1c16f2008-12-17 15:59:43 +0000108 SkASSERT(p >= buffer);
109 char* stop = buffer + sizeof(buffer);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000110 while (p < stop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000111 *string++ = *p++;
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000112 }
epoger@google.comd88a3d82013-06-19 18:27:20 +0000113 SkASSERT(string - start <= SkStrAppendU32_MaxSize);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000114 return string;
115}
116
epoger@google.comd88a3d82013-06-19 18:27:20 +0000117char* SkStrAppendS32(char string[], int32_t dec) {
mtklein225fb982014-11-05 11:35:21 -0800118 uint32_t udec = dec;
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +0000119 if (dec < 0) {
epoger@google.comd88a3d82013-06-19 18:27:20 +0000120 *string++ = '-';
mtklein225fb982014-11-05 11:35:21 -0800121 udec = ~udec + 1; // udec = -udec, but silences some warnings that are trying to be helpful
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +0000122 }
mtklein225fb982014-11-05 11:35:21 -0800123 return SkStrAppendU32(string, udec);
epoger@google.comd88a3d82013-06-19 18:27:20 +0000124}
125
126char* SkStrAppendU64(char string[], uint64_t dec, int minDigits) {
127 SkDEBUGCODE(char* start = string;)
128
129 char buffer[SkStrAppendU64_MaxSize];
130 char* p = buffer + sizeof(buffer);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000131
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +0000132 do {
caryclark@google.com803eceb2012-06-06 12:09:34 +0000133 *--p = SkToU8('0' + (int32_t) (dec % 10));
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +0000134 dec /= 10;
135 minDigits--;
136 } while (dec != 0);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000137
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +0000138 while (minDigits > 0) {
139 *--p = '0';
140 minDigits--;
141 }
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +0000142
143 SkASSERT(p >= buffer);
144 size_t cp_len = buffer + sizeof(buffer) - p;
145 memcpy(string, p, cp_len);
146 string += cp_len;
147
epoger@google.comd88a3d82013-06-19 18:27:20 +0000148 SkASSERT(string - start <= SkStrAppendU64_MaxSize);
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +0000149 return string;
150}
151
epoger@google.comd88a3d82013-06-19 18:27:20 +0000152char* SkStrAppendS64(char string[], int64_t dec, int minDigits) {
mtklein225fb982014-11-05 11:35:21 -0800153 uint64_t udec = dec;
epoger@google.comd88a3d82013-06-19 18:27:20 +0000154 if (dec < 0) {
155 *string++ = '-';
mtklein225fb982014-11-05 11:35:21 -0800156 udec = ~udec + 1; // udec = -udec, but silences some warnings that are trying to be helpful
epoger@google.comd88a3d82013-06-19 18:27:20 +0000157 }
mtklein225fb982014-11-05 11:35:21 -0800158 return SkStrAppendU64(string, udec, minDigits);
epoger@google.comd88a3d82013-06-19 18:27:20 +0000159}
160
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000161char* SkStrAppendFloat(char string[], float value) {
reed@google.com8072e4f2011-03-01 15:44:08 +0000162 // since floats have at most 8 significant digits, we limit our %g to that.
163 static const char gFormat[] = "%.8g";
164 // make it 1 larger for the terminating 0
165 char buffer[SkStrAppendScalar_MaxSize + 1];
Hal Canary09f818d2018-02-22 15:12:46 -0500166 int len = snprintf(buffer, sizeof(buffer), gFormat, value);
reed@google.com8072e4f2011-03-01 15:44:08 +0000167 memcpy(string, buffer, len);
vandebo@chromium.org677cbed2011-03-03 18:20:12 +0000168 SkASSERT(len <= SkStrAppendScalar_MaxSize);
reed@google.com8072e4f2011-03-01 15:44:08 +0000169 return string + len;
vandebo@chromium.org677cbed2011-03-03 18:20:12 +0000170}
reed@android.com8a1c16f2008-12-17 15:59:43 +0000171
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000172///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000173
Ben Wagnereccda1c2017-10-05 10:13:51 -0400174const SkString::Rec SkString::gEmptyRec(0, 0);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000175
176#define SizeOfRec() (gEmptyRec.data() - (const char*)&gEmptyRec)
177
reed@google.com8cb10882013-06-04 20:36:52 +0000178static uint32_t trim_size_t_to_u32(size_t value) {
179 if (sizeof(size_t) > sizeof(uint32_t)) {
180 if (value > SK_MaxU32) {
181 value = SK_MaxU32;
182 }
183 }
184 return (uint32_t)value;
185}
186
187static size_t check_add32(size_t base, size_t extra) {
188 SkASSERT(base <= SK_MaxU32);
189 if (sizeof(size_t) > sizeof(uint32_t)) {
190 if (base + extra > SK_MaxU32) {
191 extra = SK_MaxU32 - base;
192 }
193 }
194 return extra;
195}
196
Ben Wagnereccda1c2017-10-05 10:13:51 -0400197sk_sp<SkString::Rec> SkString::Rec::Make(const char text[], size_t len) {
reed@google.com4bce1152011-09-14 16:13:58 +0000198 if (0 == len) {
Ben Wagnereccda1c2017-10-05 10:13:51 -0400199 return sk_sp<SkString::Rec>(const_cast<Rec*>(&gEmptyRec));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000200 }
Ben Wagnereccda1c2017-10-05 10:13:51 -0400201
Mike Reed33f38b02018-02-14 13:58:06 -0500202 SkSafeMath safe;
203 // We store a 32bit version of the length
204 uint32_t stringLen = safe.castTo<uint32_t>(len);
205 // Add SizeOfRec() for our overhead and 1 for null-termination
206 size_t allocationSize = safe.add(len, SizeOfRec() + sizeof(char));
207 // Align up to a multiple of 4
208 allocationSize = safe.alignUp(allocationSize, 4);
Ben Wagnereccda1c2017-10-05 10:13:51 -0400209
Mike Reed33f38b02018-02-14 13:58:06 -0500210 SkASSERT_RELEASE(safe.ok());
211
212 void* storage = ::operator new (allocationSize);
213 sk_sp<Rec> rec(new (storage) Rec(stringLen, 1));
Ben Wagnereccda1c2017-10-05 10:13:51 -0400214 if (text) {
215 memcpy(rec->data(), text, len);
216 }
217 rec->data()[len] = 0;
Robert Phillips01f8e412017-10-05 11:28:20 +0000218 return rec;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000219}
220
Ben Wagnereccda1c2017-10-05 10:13:51 -0400221void SkString::Rec::ref() const {
222 if (this == &SkString::gEmptyRec) {
223 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000224 }
Ben Wagnereccda1c2017-10-05 10:13:51 -0400225 SkAssertResult(this->fRefCnt.fetch_add(+1, std::memory_order_relaxed));
226}
227
228void SkString::Rec::unref() const {
229 if (this == &SkString::gEmptyRec) {
230 return;
231 }
232 int32_t oldRefCnt = this->fRefCnt.fetch_add(-1, std::memory_order_acq_rel);
233 SkASSERT(oldRefCnt);
234 if (1 == oldRefCnt) {
235 delete this;
236 }
237}
238
239bool SkString::Rec::unique() const {
240 return fRefCnt.load(std::memory_order_acquire) == 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000241}
242
243#ifdef SK_DEBUG
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000244void SkString::validate() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000245 // make sure know one has written over our global
reed@google.com4bce1152011-09-14 16:13:58 +0000246 SkASSERT(0 == gEmptyRec.fLength);
Ben Wagneraf893662017-10-03 11:08:14 -0400247 SkASSERT(0 == gEmptyRec.fRefCnt.load(std::memory_order_relaxed));
reed@google.com4bce1152011-09-14 16:13:58 +0000248 SkASSERT(0 == gEmptyRec.data()[0]);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000249
Ben Wagnereccda1c2017-10-05 10:13:51 -0400250 if (fRec.get() != &gEmptyRec) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000251 SkASSERT(fRec->fLength > 0);
Ben Wagneraf893662017-10-03 11:08:14 -0400252 SkASSERT(fRec->fRefCnt.load(std::memory_order_relaxed) > 0);
reed@google.com4bce1152011-09-14 16:13:58 +0000253 SkASSERT(0 == fRec->data()[fRec->fLength]);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000254 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000255}
256#endif
257
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000258///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000259
260SkString::SkString() : fRec(const_cast<Rec*>(&gEmptyRec)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000261}
262
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000263SkString::SkString(size_t len) {
Ben Wagnereccda1c2017-10-05 10:13:51 -0400264 fRec = Rec::Make(nullptr, len);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000265}
266
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000267SkString::SkString(const char text[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000268 size_t len = text ? strlen(text) : 0;
269
Ben Wagnereccda1c2017-10-05 10:13:51 -0400270 fRec = Rec::Make(text, len);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000271}
272
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000273SkString::SkString(const char text[], size_t len) {
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 SkString& src) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000278 src.validate();
279
Ben Wagnereccda1c2017-10-05 10:13:51 -0400280 fRec = src.fRec;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000281}
282
bungeman9d552972016-02-07 18:42:54 -0800283SkString::SkString(SkString&& src) {
284 src.validate();
285
Ben Wagnereccda1c2017-10-05 10:13:51 -0400286 fRec = std::move(src.fRec);
287 src.fRec.reset(const_cast<Rec*>(&gEmptyRec));
bungeman9d552972016-02-07 18:42:54 -0800288}
289
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000290SkString::~SkString() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000291 this->validate();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000292}
293
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000294bool SkString::equals(const SkString& src) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000295 return fRec == src.fRec || this->equals(src.c_str(), src.size());
296}
297
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000298bool SkString::equals(const char text[]) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000299 return this->equals(text, text ? strlen(text) : 0);
300}
301
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000302bool SkString::equals(const char text[], size_t len) const {
halcanary96fcdcc2015-08-27 07:41:13 -0700303 SkASSERT(len == 0 || text != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000304
305 return fRec->fLength == len && !memcmp(fRec->data(), text, len);
306}
307
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000308SkString& SkString::operator=(const SkString& src) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000309 this->validate();
310
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000311 if (fRec != src.fRec) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000312 SkString tmp(src);
313 this->swap(tmp);
314 }
315 return *this;
316}
317
bungeman9d552972016-02-07 18:42:54 -0800318SkString& SkString::operator=(SkString&& src) {
319 this->validate();
320
321 if (fRec != src.fRec) {
322 this->swap(src);
323 }
324 return *this;
325}
326
bsalomon@google.comfc296292011-05-06 13:53:47 +0000327SkString& SkString::operator=(const char text[]) {
328 this->validate();
329
330 SkString tmp(text);
331 this->swap(tmp);
332
333 return *this;
334}
335
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000336void SkString::reset() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000337 this->validate();
Ben Wagnereccda1c2017-10-05 10:13:51 -0400338 fRec.reset(const_cast<Rec*>(&gEmptyRec));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000339}
340
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000341char* SkString::writable_str() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000342 this->validate();
343
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000344 if (fRec->fLength) {
Ben Wagneraf893662017-10-03 11:08:14 -0400345 if (!fRec->unique()) {
Ben Wagnereccda1c2017-10-05 10:13:51 -0400346 fRec = Rec::Make(fRec->data(), fRec->fLength);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000347 }
348 }
349 return fRec->data();
350}
351
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000352void SkString::set(const char text[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000353 this->set(text, text ? strlen(text) : 0);
354}
355
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000356void SkString::set(const char text[], size_t len) {
reed@google.com8cb10882013-06-04 20:36:52 +0000357 len = trim_size_t_to_u32(len);
Ben Wagneraf893662017-10-03 11:08:14 -0400358 bool unique = fRec->unique();
reed@google.com4bce1152011-09-14 16:13:58 +0000359 if (0 == len) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000360 this->reset();
Ben Wagneraf893662017-10-03 11:08:14 -0400361 } else if (unique && len <= fRec->fLength) {
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000362 // should we resize if len <<<< fLength, to save RAM? (e.g. len < (fLength>>1))?
reed@android.com8a1c16f2008-12-17 15:59:43 +0000363 // just use less of the buffer without allocating a smaller one
364 char* p = this->writable_str();
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000365 if (text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000366 memcpy(p, text, len);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000367 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000368 p[len] = 0;
reed@google.com8cb10882013-06-04 20:36:52 +0000369 fRec->fLength = SkToU32(len);
Ben Wagneraf893662017-10-03 11:08:14 -0400370 } else if (unique && (fRec->fLength >> 2) == (len >> 2)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000371 // we have spare room in the current allocation, so don't alloc a larger one
372 char* p = this->writable_str();
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000373 if (text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000374 memcpy(p, text, len);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000375 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000376 p[len] = 0;
reed@google.com8cb10882013-06-04 20:36:52 +0000377 fRec->fLength = SkToU32(len);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000378 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000379 SkString tmp(text, len);
380 this->swap(tmp);
381 }
382}
383
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000384void SkString::insert(size_t offset, const char text[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000385 this->insert(offset, text, text ? strlen(text) : 0);
386}
387
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000388void SkString::insert(size_t offset, const char text[], size_t len) {
389 if (len) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000390 size_t length = fRec->fLength;
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000391 if (offset > length) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000392 offset = length;
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000393 }
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +0000394
reed@google.com8cb10882013-06-04 20:36:52 +0000395 // Check if length + len exceeds 32bits, we trim len
396 len = check_add32(length, len);
397 if (0 == len) {
398 return;
399 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000400
401 /* If we're the only owner, and we have room in our allocation for the insert,
402 do it in place, rather than allocating a new buffer.
403
404 To know we have room, compare the allocated sizes
405 beforeAlloc = SkAlign4(length + 1)
406 afterAlloc = SkAligh4(length + 1 + len)
407 but SkAlign4(x) is (x + 3) >> 2 << 2
408 which is equivalent for testing to (length + 1 + 3) >> 2 == (length + 1 + 3 + len) >> 2
409 and we can then eliminate the +1+3 since that doesn't affec the answer
410 */
Ben Wagneraf893662017-10-03 11:08:14 -0400411 if (fRec->unique() && (length >> 2) == ((length + len) >> 2)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000412 char* dst = this->writable_str();
413
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000414 if (offset < length) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000415 memmove(dst + offset + len, dst + offset, length - offset);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000416 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000417 memcpy(dst + offset, text, len);
418
419 dst[length + len] = 0;
reed@google.com8cb10882013-06-04 20:36:52 +0000420 fRec->fLength = SkToU32(length + len);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000421 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000422 /* Seems we should use realloc here, since that is safe if it fails
423 (we have the original data), and might be faster than alloc/copy/free.
424 */
425 SkString tmp(fRec->fLength + len);
426 char* dst = tmp.writable_str();
427
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000428 if (offset > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000429 memcpy(dst, fRec->data(), offset);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000430 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000431 memcpy(dst + offset, text, len);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000432 if (offset < fRec->fLength) {
433 memcpy(dst + offset + len, fRec->data() + offset,
434 fRec->fLength - offset);
435 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000436
437 this->swap(tmp);
438 }
439 }
440}
441
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000442void SkString::insertUnichar(size_t offset, SkUnichar uni) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000443 char buffer[kMaxBytesInUTF8Sequence];
444 size_t len = SkUTF8_FromUnichar(uni, buffer);
445
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000446 if (len) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000447 this->insert(offset, buffer, len);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000448 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000449}
450
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000451void SkString::insertS32(size_t offset, int32_t dec) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000452 char buffer[SkStrAppendS32_MaxSize];
453 char* stop = SkStrAppendS32(buffer, dec);
454 this->insert(offset, buffer, stop - buffer);
455}
456
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000457void SkString::insertS64(size_t offset, int64_t dec, int minDigits) {
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +0000458 char buffer[SkStrAppendS64_MaxSize];
459 char* stop = SkStrAppendS64(buffer, dec, minDigits);
460 this->insert(offset, buffer, stop - buffer);
461}
462
epoger@google.comd88a3d82013-06-19 18:27:20 +0000463void SkString::insertU32(size_t offset, uint32_t dec) {
464 char buffer[SkStrAppendU32_MaxSize];
465 char* stop = SkStrAppendU32(buffer, dec);
466 this->insert(offset, buffer, stop - buffer);
467}
468
469void SkString::insertU64(size_t offset, uint64_t dec, int minDigits) {
470 char buffer[SkStrAppendU64_MaxSize];
471 char* stop = SkStrAppendU64(buffer, dec, minDigits);
472 this->insert(offset, buffer, stop - buffer);
473}
474
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000475void SkString::insertHex(size_t offset, uint32_t hex, int minDigits) {
bungeman62ce0302015-08-28 09:09:32 -0700476 minDigits = SkTPin(minDigits, 0, 8);
reed@google.comfa06e522011-02-28 21:29:58 +0000477
reed@android.com8a1c16f2008-12-17 15:59:43 +0000478 char buffer[8];
479 char* p = buffer + sizeof(buffer);
480
481 do {
Hal Canaryd6e6e662017-06-17 10:38:13 -0400482 *--p = SkHexadecimalDigits::gUpper[hex & 0xF];
reed@android.com8a1c16f2008-12-17 15:59:43 +0000483 hex >>= 4;
484 minDigits -= 1;
485 } while (hex != 0);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000486
487 while (--minDigits >= 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000488 *--p = '0';
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000489 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000490
491 SkASSERT(p >= buffer);
492 this->insert(offset, p, buffer + sizeof(buffer) - p);
493}
494
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000495void SkString::insertScalar(size_t offset, SkScalar value) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000496 char buffer[SkStrAppendScalar_MaxSize];
497 char* stop = SkStrAppendScalar(buffer, value);
498 this->insert(offset, buffer, stop - buffer);
499}
500
reed@android.com8a1c16f2008-12-17 15:59:43 +0000501void SkString::printf(const char format[], ...) {
halcanaryd51bdae2016-04-25 09:25:35 -0700502 V_SKSTRING_PRINTF((*this), format);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000503}
504
505void SkString::appendf(const char format[], ...) {
Brian Salomon43227522018-04-25 12:58:23 -0400506 char buffer[kBufferSize];
joshualitt976386b2014-10-23 18:23:32 -0700507 int length;
Brian Salomon43227522018-04-25 12:58:23 -0400508 const char* result;
509 ARGS_TO_BUFFER(format, buffer, kBufferSize, length, result);
reed@google.comfa06e522011-02-28 21:29:58 +0000510
Brian Salomon43227522018-04-25 12:58:23 -0400511 this->append(result, length);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000512}
513
commit-bot@chromium.orgce0e4ef2013-11-21 17:20:17 +0000514void SkString::appendVAList(const char format[], va_list args) {
Brian Salomon43227522018-04-25 12:58:23 -0400515 char buffer[kBufferSize];
Hal Canary09f818d2018-02-22 15:12:46 -0500516 int length = vsnprintf(buffer, kBufferSize, format, args);
joshualitt976386b2014-10-23 18:23:32 -0700517 SkASSERT(length >= 0 && length < SkToInt(kBufferSize));
bsalomon@google.comf910d3b2013-03-07 17:06:57 +0000518
joshualitt976386b2014-10-23 18:23:32 -0700519 this->append(buffer, length);
bsalomon@google.comf910d3b2013-03-07 17:06:57 +0000520}
521
reed@android.com8a1c16f2008-12-17 15:59:43 +0000522void SkString::prependf(const char format[], ...) {
Brian Salomon43227522018-04-25 12:58:23 -0400523 char buffer[kBufferSize];
joshualitt976386b2014-10-23 18:23:32 -0700524 int length;
Brian Salomon43227522018-04-25 12:58:23 -0400525 const char* result;
526 ARGS_TO_BUFFER(format, buffer, kBufferSize, length, result);
reed@google.comfa06e522011-02-28 21:29:58 +0000527
Brian Salomon43227522018-04-25 12:58:23 -0400528 this->prepend(result, length);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000529}
530
joshualitt30ba4362014-08-21 20:18:45 -0700531void SkString::prependVAList(const char format[], va_list args) {
Brian Salomon43227522018-04-25 12:58:23 -0400532 char buffer[kBufferSize];
Hal Canary09f818d2018-02-22 15:12:46 -0500533 int length = vsnprintf(buffer, kBufferSize, format, args);
joshualitt976386b2014-10-23 18:23:32 -0700534 SkASSERT(length >= 0 && length < SkToInt(kBufferSize));
joshualitt30ba4362014-08-21 20:18:45 -0700535
joshualitt976386b2014-10-23 18:23:32 -0700536 this->prepend(buffer, length);
joshualitt30ba4362014-08-21 20:18:45 -0700537}
538
539
reed@google.comfa06e522011-02-28 21:29:58 +0000540///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000541
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000542void SkString::remove(size_t offset, size_t length) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000543 size_t size = this->size();
544
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000545 if (offset < size) {
bungemandfb9bc42014-08-11 07:19:56 -0700546 if (length > size - offset) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000547 length = size - offset;
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000548 }
bungemandfb9bc42014-08-11 07:19:56 -0700549 SkASSERT(length <= size);
550 SkASSERT(offset <= size - length);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000551 if (length > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000552 SkString tmp(size - length);
553 char* dst = tmp.writable_str();
554 const char* src = this->c_str();
555
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000556 if (offset) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000557 memcpy(dst, src, offset);
558 }
bungemandfb9bc42014-08-11 07:19:56 -0700559 size_t tail = size - (offset + length);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000560 if (tail) {
bungemandfb9bc42014-08-11 07:19:56 -0700561 memcpy(dst + offset, src + (offset + length), tail);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000562 }
563 SkASSERT(dst[tmp.size()] == 0);
564 this->swap(tmp);
565 }
566 }
567}
568
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000569void SkString::swap(SkString& other) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000570 this->validate();
571 other.validate();
572
Ben Wagnereccda1c2017-10-05 10:13:51 -0400573 SkTSwap(fRec, other.fRec);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000574}
575
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000576///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000577
tomhudson@google.com3a1f6a02011-06-30 14:39:52 +0000578SkString SkStringPrintf(const char* format, ...) {
579 SkString formattedOutput;
halcanaryd51bdae2016-04-25 09:25:35 -0700580 V_SKSTRING_PRINTF(formattedOutput, format);
tomhudson@google.com3a1f6a02011-06-30 14:39:52 +0000581 return formattedOutput;
582}
583
kkinnunen3e980c32015-12-23 01:33:00 -0800584void SkStrSplit(const char* str, const char* delimiters, SkStrSplitMode splitMode,
585 SkTArray<SkString>* out) {
586 if (splitMode == kCoalesce_SkStrSplitMode) {
rmistry0f515bd2015-12-22 10:22:26 -0800587 // Skip any delimiters.
588 str += strspn(str, delimiters);
kkinnunen9ebc3f02015-12-21 23:48:13 -0800589 }
kkinnunen3e980c32015-12-23 01:33:00 -0800590 if (!*str) {
591 return;
592 }
593
594 while (true) {
595 // Find a token.
596 const size_t len = strcspn(str, delimiters);
597 if (splitMode == kStrict_SkStrSplitMode || len > 0) {
598 out->push_back().set(str, len);
599 str += len;
600 }
601
602 if (!*str) {
603 return;
604 }
605 if (splitMode == kCoalesce_SkStrSplitMode) {
606 // Skip any delimiters.
607 str += strspn(str, delimiters);
608 } else {
609 // Skip one delimiter.
610 str += 1;
611 }
612 }
rmistry@google.comd6bab022013-12-02 13:50:38 +0000613}