blob: 1242b3333d7a80cce0de3df3731452a7354acfb0 [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
herbb906daf2015-09-29 09:37:59 -07008#include "SkAtomics.h"
Mike Reed33f38b02018-02-14 13:58:06 -05009#include "SkSafeMath.h"
mtklein1b249332015-07-07 12:21:21 -070010#include "SkString.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000011#include "SkUtils.h"
12#include <stdarg.h>
reed@google.comfa06e522011-02-28 21:29:58 +000013#include <stdio.h>
14
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
joshualitt976386b2014-10-23 18:23:32 -070018#define ARGS_TO_BUFFER(format, buffer, size, written) \
19 do { \
20 va_list args; \
21 va_start(args, format); \
Hal Canary09f818d2018-02-22 15:12:46 -050022 written = vsnprintf(buffer, size, format, args); \
joshualitt976386b2014-10-23 18:23:32 -070023 SkASSERT(written >= 0 && written < SkToInt(size)); \
24 va_end(args); \
reed@google.comfa06e522011-02-28 21:29:58 +000025 } while (0)
26
halcanaryd51bdae2016-04-25 09:25:35 -070027#define V_SKSTRING_PRINTF(output, format) \
28 do { \
29 va_list args; \
30 va_start(args, format); \
31 char buffer[kBufferSize]; \
32 int length = vsnprintf(buffer, sizeof(buffer), format, args); \
33 va_end(args); \
34 if (length < 0) { \
35 break; \
36 } \
37 if (length < (int)sizeof(buffer)) { \
38 output.set(buffer, length); \
39 break; \
40 } \
Hal Canary09f818d2018-02-22 15:12:46 -050041 SkString tmp((size_t)length); \
halcanaryd51bdae2016-04-25 09:25:35 -070042 va_start(args, format); \
Hal Canary09f818d2018-02-22 15:12:46 -050043 SkDEBUGCODE(int check = ) vsnprintf(tmp.writable_str(), \
halcanaryd51bdae2016-04-25 09:25:35 -070044 length + 1, format, args); \
45 va_end(args); \
46 SkASSERT(check == length); \
Hal Canary09f818d2018-02-22 15:12:46 -050047 output = std::move(tmp); \
halcanaryd51bdae2016-04-25 09:25:35 -070048 SkASSERT(output[length] == '\0'); \
49 } while (false)
halcanaryd51bdae2016-04-25 09:25:35 -070050
reed@google.comfa06e522011-02-28 21:29:58 +000051///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +000052
epoger@google.come8ebeb12012-10-29 16:42:11 +000053bool SkStrEndsWith(const char string[], const char suffixStr[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000054 SkASSERT(string);
epoger@google.come8ebeb12012-10-29 16:42:11 +000055 SkASSERT(suffixStr);
reed@android.com8a1c16f2008-12-17 15:59:43 +000056 size_t strLen = strlen(string);
epoger@google.come8ebeb12012-10-29 16:42:11 +000057 size_t suffixLen = strlen(suffixStr);
reed@android.com8a1c16f2008-12-17 15:59:43 +000058 return strLen >= suffixLen &&
epoger@google.come8ebeb12012-10-29 16:42:11 +000059 !strncmp(string + strLen - suffixLen, suffixStr, suffixLen);
60}
61
62bool SkStrEndsWith(const char string[], const char suffixChar) {
63 SkASSERT(string);
64 size_t strLen = strlen(string);
65 if (0 == strLen) {
66 return false;
67 } else {
68 return (suffixChar == string[strLen-1]);
69 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000070}
71
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +000072int SkStrStartsWithOneOf(const char string[], const char prefixes[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000073 int index = 0;
74 do {
75 const char* limit = strchr(prefixes, '\0');
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +000076 if (!strncmp(string, prefixes, limit - prefixes)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000077 return index;
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +000078 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000079 prefixes = limit + 1;
80 index++;
81 } while (prefixes[0]);
82 return -1;
83}
84
epoger@google.comd88a3d82013-06-19 18:27:20 +000085char* SkStrAppendU32(char string[], uint32_t dec) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000086 SkDEBUGCODE(char* start = string;)
87
epoger@google.comd88a3d82013-06-19 18:27:20 +000088 char buffer[SkStrAppendU32_MaxSize];
reed@android.com8a1c16f2008-12-17 15:59:43 +000089 char* p = buffer + sizeof(buffer);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +000090
reed@android.com8a1c16f2008-12-17 15:59:43 +000091 do {
92 *--p = SkToU8('0' + dec % 10);
93 dec /= 10;
94 } while (dec != 0);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +000095
reed@android.com8a1c16f2008-12-17 15:59:43 +000096 SkASSERT(p >= buffer);
97 char* stop = buffer + sizeof(buffer);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +000098 while (p < stop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000099 *string++ = *p++;
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000100 }
epoger@google.comd88a3d82013-06-19 18:27:20 +0000101 SkASSERT(string - start <= SkStrAppendU32_MaxSize);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000102 return string;
103}
104
epoger@google.comd88a3d82013-06-19 18:27:20 +0000105char* SkStrAppendS32(char string[], int32_t dec) {
mtklein225fb982014-11-05 11:35:21 -0800106 uint32_t udec = dec;
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +0000107 if (dec < 0) {
epoger@google.comd88a3d82013-06-19 18:27:20 +0000108 *string++ = '-';
mtklein225fb982014-11-05 11:35:21 -0800109 udec = ~udec + 1; // udec = -udec, but silences some warnings that are trying to be helpful
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +0000110 }
mtklein225fb982014-11-05 11:35:21 -0800111 return SkStrAppendU32(string, udec);
epoger@google.comd88a3d82013-06-19 18:27:20 +0000112}
113
114char* SkStrAppendU64(char string[], uint64_t dec, int minDigits) {
115 SkDEBUGCODE(char* start = string;)
116
117 char buffer[SkStrAppendU64_MaxSize];
118 char* p = buffer + sizeof(buffer);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000119
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +0000120 do {
caryclark@google.com803eceb2012-06-06 12:09:34 +0000121 *--p = SkToU8('0' + (int32_t) (dec % 10));
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +0000122 dec /= 10;
123 minDigits--;
124 } while (dec != 0);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000125
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +0000126 while (minDigits > 0) {
127 *--p = '0';
128 minDigits--;
129 }
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +0000130
131 SkASSERT(p >= buffer);
132 size_t cp_len = buffer + sizeof(buffer) - p;
133 memcpy(string, p, cp_len);
134 string += cp_len;
135
epoger@google.comd88a3d82013-06-19 18:27:20 +0000136 SkASSERT(string - start <= SkStrAppendU64_MaxSize);
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +0000137 return string;
138}
139
epoger@google.comd88a3d82013-06-19 18:27:20 +0000140char* SkStrAppendS64(char string[], int64_t dec, int minDigits) {
mtklein225fb982014-11-05 11:35:21 -0800141 uint64_t udec = dec;
epoger@google.comd88a3d82013-06-19 18:27:20 +0000142 if (dec < 0) {
143 *string++ = '-';
mtklein225fb982014-11-05 11:35:21 -0800144 udec = ~udec + 1; // udec = -udec, but silences some warnings that are trying to be helpful
epoger@google.comd88a3d82013-06-19 18:27:20 +0000145 }
mtklein225fb982014-11-05 11:35:21 -0800146 return SkStrAppendU64(string, udec, minDigits);
epoger@google.comd88a3d82013-06-19 18:27:20 +0000147}
148
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000149char* SkStrAppendFloat(char string[], float value) {
reed@google.com8072e4f2011-03-01 15:44:08 +0000150 // since floats have at most 8 significant digits, we limit our %g to that.
151 static const char gFormat[] = "%.8g";
152 // make it 1 larger for the terminating 0
153 char buffer[SkStrAppendScalar_MaxSize + 1];
Hal Canary09f818d2018-02-22 15:12:46 -0500154 int len = snprintf(buffer, sizeof(buffer), gFormat, value);
reed@google.com8072e4f2011-03-01 15:44:08 +0000155 memcpy(string, buffer, len);
vandebo@chromium.org677cbed2011-03-03 18:20:12 +0000156 SkASSERT(len <= SkStrAppendScalar_MaxSize);
reed@google.com8072e4f2011-03-01 15:44:08 +0000157 return string + len;
vandebo@chromium.org677cbed2011-03-03 18:20:12 +0000158}
reed@android.com8a1c16f2008-12-17 15:59:43 +0000159
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000160///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000161
Ben Wagnereccda1c2017-10-05 10:13:51 -0400162const SkString::Rec SkString::gEmptyRec(0, 0);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000163
164#define SizeOfRec() (gEmptyRec.data() - (const char*)&gEmptyRec)
165
reed@google.com8cb10882013-06-04 20:36:52 +0000166static uint32_t trim_size_t_to_u32(size_t value) {
167 if (sizeof(size_t) > sizeof(uint32_t)) {
168 if (value > SK_MaxU32) {
169 value = SK_MaxU32;
170 }
171 }
172 return (uint32_t)value;
173}
174
175static size_t check_add32(size_t base, size_t extra) {
176 SkASSERT(base <= SK_MaxU32);
177 if (sizeof(size_t) > sizeof(uint32_t)) {
178 if (base + extra > SK_MaxU32) {
179 extra = SK_MaxU32 - base;
180 }
181 }
182 return extra;
183}
184
Ben Wagnereccda1c2017-10-05 10:13:51 -0400185sk_sp<SkString::Rec> SkString::Rec::Make(const char text[], size_t len) {
reed@google.com4bce1152011-09-14 16:13:58 +0000186 if (0 == len) {
Ben Wagnereccda1c2017-10-05 10:13:51 -0400187 return sk_sp<SkString::Rec>(const_cast<Rec*>(&gEmptyRec));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000188 }
Ben Wagnereccda1c2017-10-05 10:13:51 -0400189
Mike Reed33f38b02018-02-14 13:58:06 -0500190 SkSafeMath safe;
191 // We store a 32bit version of the length
192 uint32_t stringLen = safe.castTo<uint32_t>(len);
193 // Add SizeOfRec() for our overhead and 1 for null-termination
194 size_t allocationSize = safe.add(len, SizeOfRec() + sizeof(char));
195 // Align up to a multiple of 4
196 allocationSize = safe.alignUp(allocationSize, 4);
Ben Wagnereccda1c2017-10-05 10:13:51 -0400197
Mike Reed33f38b02018-02-14 13:58:06 -0500198 SkASSERT_RELEASE(safe.ok());
199
200 void* storage = ::operator new (allocationSize);
201 sk_sp<Rec> rec(new (storage) Rec(stringLen, 1));
Ben Wagnereccda1c2017-10-05 10:13:51 -0400202 if (text) {
203 memcpy(rec->data(), text, len);
204 }
205 rec->data()[len] = 0;
Robert Phillips01f8e412017-10-05 11:28:20 +0000206 return rec;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000207}
208
Ben Wagnereccda1c2017-10-05 10:13:51 -0400209void SkString::Rec::ref() const {
210 if (this == &SkString::gEmptyRec) {
211 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000212 }
Ben Wagnereccda1c2017-10-05 10:13:51 -0400213 SkAssertResult(this->fRefCnt.fetch_add(+1, std::memory_order_relaxed));
214}
215
216void SkString::Rec::unref() const {
217 if (this == &SkString::gEmptyRec) {
218 return;
219 }
220 int32_t oldRefCnt = this->fRefCnt.fetch_add(-1, std::memory_order_acq_rel);
221 SkASSERT(oldRefCnt);
222 if (1 == oldRefCnt) {
223 delete this;
224 }
225}
226
227bool SkString::Rec::unique() const {
228 return fRefCnt.load(std::memory_order_acquire) == 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000229}
230
231#ifdef SK_DEBUG
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000232void SkString::validate() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000233 // make sure know one has written over our global
reed@google.com4bce1152011-09-14 16:13:58 +0000234 SkASSERT(0 == gEmptyRec.fLength);
Ben Wagneraf893662017-10-03 11:08:14 -0400235 SkASSERT(0 == gEmptyRec.fRefCnt.load(std::memory_order_relaxed));
reed@google.com4bce1152011-09-14 16:13:58 +0000236 SkASSERT(0 == gEmptyRec.data()[0]);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000237
Ben Wagnereccda1c2017-10-05 10:13:51 -0400238 if (fRec.get() != &gEmptyRec) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000239 SkASSERT(fRec->fLength > 0);
Ben Wagneraf893662017-10-03 11:08:14 -0400240 SkASSERT(fRec->fRefCnt.load(std::memory_order_relaxed) > 0);
reed@google.com4bce1152011-09-14 16:13:58 +0000241 SkASSERT(0 == fRec->data()[fRec->fLength]);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000242 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000243}
244#endif
245
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000246///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000247
248SkString::SkString() : fRec(const_cast<Rec*>(&gEmptyRec)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000249}
250
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000251SkString::SkString(size_t len) {
Ben Wagnereccda1c2017-10-05 10:13:51 -0400252 fRec = Rec::Make(nullptr, len);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000253}
254
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000255SkString::SkString(const char text[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000256 size_t len = text ? strlen(text) : 0;
257
Ben Wagnereccda1c2017-10-05 10:13:51 -0400258 fRec = Rec::Make(text, len);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000259}
260
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000261SkString::SkString(const char text[], size_t len) {
Ben Wagnereccda1c2017-10-05 10:13:51 -0400262 fRec = Rec::Make(text, len);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000263}
264
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000265SkString::SkString(const SkString& src) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000266 src.validate();
267
Ben Wagnereccda1c2017-10-05 10:13:51 -0400268 fRec = src.fRec;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000269}
270
bungeman9d552972016-02-07 18:42:54 -0800271SkString::SkString(SkString&& src) {
272 src.validate();
273
Ben Wagnereccda1c2017-10-05 10:13:51 -0400274 fRec = std::move(src.fRec);
275 src.fRec.reset(const_cast<Rec*>(&gEmptyRec));
bungeman9d552972016-02-07 18:42:54 -0800276}
277
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000278SkString::~SkString() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000279 this->validate();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000280}
281
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000282bool SkString::equals(const SkString& src) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000283 return fRec == src.fRec || this->equals(src.c_str(), src.size());
284}
285
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000286bool SkString::equals(const char text[]) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000287 return this->equals(text, text ? strlen(text) : 0);
288}
289
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000290bool SkString::equals(const char text[], size_t len) const {
halcanary96fcdcc2015-08-27 07:41:13 -0700291 SkASSERT(len == 0 || text != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000292
293 return fRec->fLength == len && !memcmp(fRec->data(), text, len);
294}
295
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000296SkString& SkString::operator=(const SkString& src) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000297 this->validate();
298
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000299 if (fRec != src.fRec) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000300 SkString tmp(src);
301 this->swap(tmp);
302 }
303 return *this;
304}
305
bungeman9d552972016-02-07 18:42:54 -0800306SkString& SkString::operator=(SkString&& src) {
307 this->validate();
308
309 if (fRec != src.fRec) {
310 this->swap(src);
311 }
312 return *this;
313}
314
bsalomon@google.comfc296292011-05-06 13:53:47 +0000315SkString& SkString::operator=(const char text[]) {
316 this->validate();
317
318 SkString tmp(text);
319 this->swap(tmp);
320
321 return *this;
322}
323
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000324void SkString::reset() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000325 this->validate();
Ben Wagnereccda1c2017-10-05 10:13:51 -0400326 fRec.reset(const_cast<Rec*>(&gEmptyRec));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000327}
328
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000329char* SkString::writable_str() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000330 this->validate();
331
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000332 if (fRec->fLength) {
Ben Wagneraf893662017-10-03 11:08:14 -0400333 if (!fRec->unique()) {
Ben Wagnereccda1c2017-10-05 10:13:51 -0400334 fRec = Rec::Make(fRec->data(), fRec->fLength);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000335 }
336 }
337 return fRec->data();
338}
339
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000340void SkString::set(const char text[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000341 this->set(text, text ? strlen(text) : 0);
342}
343
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000344void SkString::set(const char text[], size_t len) {
reed@google.com8cb10882013-06-04 20:36:52 +0000345 len = trim_size_t_to_u32(len);
Ben Wagneraf893662017-10-03 11:08:14 -0400346 bool unique = fRec->unique();
reed@google.com4bce1152011-09-14 16:13:58 +0000347 if (0 == len) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000348 this->reset();
Ben Wagneraf893662017-10-03 11:08:14 -0400349 } else if (unique && len <= fRec->fLength) {
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000350 // should we resize if len <<<< fLength, to save RAM? (e.g. len < (fLength>>1))?
reed@android.com8a1c16f2008-12-17 15:59:43 +0000351 // just use less of the buffer without allocating a smaller one
352 char* p = this->writable_str();
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000353 if (text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000354 memcpy(p, text, len);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000355 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000356 p[len] = 0;
reed@google.com8cb10882013-06-04 20:36:52 +0000357 fRec->fLength = SkToU32(len);
Ben Wagneraf893662017-10-03 11:08:14 -0400358 } else if (unique && (fRec->fLength >> 2) == (len >> 2)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000359 // we have spare room in the current allocation, so don't alloc a larger one
360 char* p = this->writable_str();
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000361 if (text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000362 memcpy(p, text, len);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000363 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000364 p[len] = 0;
reed@google.com8cb10882013-06-04 20:36:52 +0000365 fRec->fLength = SkToU32(len);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000366 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000367 SkString tmp(text, len);
368 this->swap(tmp);
369 }
370}
371
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000372void SkString::setUTF16(const uint16_t src[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000373 int count = 0;
374
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000375 while (src[count]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000376 count += 1;
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000377 }
reed@google.com8cb10882013-06-04 20:36:52 +0000378 this->setUTF16(src, count);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000379}
380
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000381void SkString::setUTF16(const uint16_t src[], size_t count) {
reed@google.com8cb10882013-06-04 20:36:52 +0000382 count = trim_size_t_to_u32(count);
383
reed@google.com4bce1152011-09-14 16:13:58 +0000384 if (0 == count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000385 this->reset();
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000386 } else if (count <= fRec->fLength) {
387 // should we resize if len <<<< fLength, to save RAM? (e.g. len < (fLength>>1))
388 if (count < fRec->fLength) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000389 this->resize(count);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000390 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000391 char* p = this->writable_str();
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000392 for (size_t i = 0; i < count; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000393 p[i] = SkToU8(src[i]);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000394 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000395 p[count] = 0;
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000396 } else {
397 SkString tmp(count); // puts a null terminator at the end of the string
398 char* p = tmp.writable_str();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000399
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000400 for (size_t i = 0; i < count; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000401 p[i] = SkToU8(src[i]);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000402 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000403 this->swap(tmp);
404 }
405}
406
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000407void SkString::insert(size_t offset, const char text[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000408 this->insert(offset, text, text ? strlen(text) : 0);
409}
410
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000411void SkString::insert(size_t offset, const char text[], size_t len) {
412 if (len) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000413 size_t length = fRec->fLength;
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000414 if (offset > length) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000415 offset = length;
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000416 }
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +0000417
reed@google.com8cb10882013-06-04 20:36:52 +0000418 // Check if length + len exceeds 32bits, we trim len
419 len = check_add32(length, len);
420 if (0 == len) {
421 return;
422 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000423
424 /* If we're the only owner, and we have room in our allocation for the insert,
425 do it in place, rather than allocating a new buffer.
426
427 To know we have room, compare the allocated sizes
428 beforeAlloc = SkAlign4(length + 1)
429 afterAlloc = SkAligh4(length + 1 + len)
430 but SkAlign4(x) is (x + 3) >> 2 << 2
431 which is equivalent for testing to (length + 1 + 3) >> 2 == (length + 1 + 3 + len) >> 2
432 and we can then eliminate the +1+3 since that doesn't affec the answer
433 */
Ben Wagneraf893662017-10-03 11:08:14 -0400434 if (fRec->unique() && (length >> 2) == ((length + len) >> 2)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000435 char* dst = this->writable_str();
436
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000437 if (offset < length) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000438 memmove(dst + offset + len, dst + offset, length - offset);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000439 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000440 memcpy(dst + offset, text, len);
441
442 dst[length + len] = 0;
reed@google.com8cb10882013-06-04 20:36:52 +0000443 fRec->fLength = SkToU32(length + len);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000444 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000445 /* Seems we should use realloc here, since that is safe if it fails
446 (we have the original data), and might be faster than alloc/copy/free.
447 */
448 SkString tmp(fRec->fLength + len);
449 char* dst = tmp.writable_str();
450
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000451 if (offset > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000452 memcpy(dst, fRec->data(), offset);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000453 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000454 memcpy(dst + offset, text, len);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000455 if (offset < fRec->fLength) {
456 memcpy(dst + offset + len, fRec->data() + offset,
457 fRec->fLength - offset);
458 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000459
460 this->swap(tmp);
461 }
462 }
463}
464
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000465void SkString::insertUnichar(size_t offset, SkUnichar uni) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000466 char buffer[kMaxBytesInUTF8Sequence];
467 size_t len = SkUTF8_FromUnichar(uni, buffer);
468
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000469 if (len) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000470 this->insert(offset, buffer, len);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000471 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000472}
473
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000474void SkString::insertS32(size_t offset, int32_t dec) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000475 char buffer[SkStrAppendS32_MaxSize];
476 char* stop = SkStrAppendS32(buffer, dec);
477 this->insert(offset, buffer, stop - buffer);
478}
479
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000480void SkString::insertS64(size_t offset, int64_t dec, int minDigits) {
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +0000481 char buffer[SkStrAppendS64_MaxSize];
482 char* stop = SkStrAppendS64(buffer, dec, minDigits);
483 this->insert(offset, buffer, stop - buffer);
484}
485
epoger@google.comd88a3d82013-06-19 18:27:20 +0000486void SkString::insertU32(size_t offset, uint32_t dec) {
487 char buffer[SkStrAppendU32_MaxSize];
488 char* stop = SkStrAppendU32(buffer, dec);
489 this->insert(offset, buffer, stop - buffer);
490}
491
492void SkString::insertU64(size_t offset, uint64_t dec, int minDigits) {
493 char buffer[SkStrAppendU64_MaxSize];
494 char* stop = SkStrAppendU64(buffer, dec, minDigits);
495 this->insert(offset, buffer, stop - buffer);
496}
497
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000498void SkString::insertHex(size_t offset, uint32_t hex, int minDigits) {
bungeman62ce0302015-08-28 09:09:32 -0700499 minDigits = SkTPin(minDigits, 0, 8);
reed@google.comfa06e522011-02-28 21:29:58 +0000500
reed@android.com8a1c16f2008-12-17 15:59:43 +0000501 char buffer[8];
502 char* p = buffer + sizeof(buffer);
503
504 do {
Hal Canaryd6e6e662017-06-17 10:38:13 -0400505 *--p = SkHexadecimalDigits::gUpper[hex & 0xF];
reed@android.com8a1c16f2008-12-17 15:59:43 +0000506 hex >>= 4;
507 minDigits -= 1;
508 } while (hex != 0);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000509
510 while (--minDigits >= 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000511 *--p = '0';
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000512 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000513
514 SkASSERT(p >= buffer);
515 this->insert(offset, p, buffer + sizeof(buffer) - p);
516}
517
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000518void SkString::insertScalar(size_t offset, SkScalar value) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000519 char buffer[SkStrAppendScalar_MaxSize];
520 char* stop = SkStrAppendScalar(buffer, value);
521 this->insert(offset, buffer, stop - buffer);
522}
523
reed@android.com8a1c16f2008-12-17 15:59:43 +0000524void SkString::printf(const char format[], ...) {
halcanaryd51bdae2016-04-25 09:25:35 -0700525 V_SKSTRING_PRINTF((*this), format);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000526}
527
528void SkString::appendf(const char format[], ...) {
529 char buffer[kBufferSize];
joshualitt976386b2014-10-23 18:23:32 -0700530 int length;
531 ARGS_TO_BUFFER(format, buffer, kBufferSize, length);
reed@google.comfa06e522011-02-28 21:29:58 +0000532
joshualitt976386b2014-10-23 18:23:32 -0700533 this->append(buffer, length);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000534}
535
commit-bot@chromium.orgce0e4ef2013-11-21 17:20:17 +0000536void SkString::appendVAList(const char format[], va_list args) {
bsalomon@google.comf910d3b2013-03-07 17:06:57 +0000537 char buffer[kBufferSize];
Hal Canary09f818d2018-02-22 15:12:46 -0500538 int length = vsnprintf(buffer, kBufferSize, format, args);
joshualitt976386b2014-10-23 18:23:32 -0700539 SkASSERT(length >= 0 && length < SkToInt(kBufferSize));
bsalomon@google.comf910d3b2013-03-07 17:06:57 +0000540
joshualitt976386b2014-10-23 18:23:32 -0700541 this->append(buffer, length);
bsalomon@google.comf910d3b2013-03-07 17:06:57 +0000542}
543
reed@android.com8a1c16f2008-12-17 15:59:43 +0000544void SkString::prependf(const char format[], ...) {
545 char buffer[kBufferSize];
joshualitt976386b2014-10-23 18:23:32 -0700546 int length;
547 ARGS_TO_BUFFER(format, buffer, kBufferSize, length);
reed@google.comfa06e522011-02-28 21:29:58 +0000548
joshualitt976386b2014-10-23 18:23:32 -0700549 this->prepend(buffer, length);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000550}
551
joshualitt30ba4362014-08-21 20:18:45 -0700552void SkString::prependVAList(const char format[], va_list args) {
553 char buffer[kBufferSize];
Hal Canary09f818d2018-02-22 15:12:46 -0500554 int length = vsnprintf(buffer, kBufferSize, format, args);
joshualitt976386b2014-10-23 18:23:32 -0700555 SkASSERT(length >= 0 && length < SkToInt(kBufferSize));
joshualitt30ba4362014-08-21 20:18:45 -0700556
joshualitt976386b2014-10-23 18:23:32 -0700557 this->prepend(buffer, length);
joshualitt30ba4362014-08-21 20:18:45 -0700558}
559
560
reed@google.comfa06e522011-02-28 21:29:58 +0000561///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000562
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000563void SkString::remove(size_t offset, size_t length) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000564 size_t size = this->size();
565
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000566 if (offset < size) {
bungemandfb9bc42014-08-11 07:19:56 -0700567 if (length > size - offset) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000568 length = size - offset;
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000569 }
bungemandfb9bc42014-08-11 07:19:56 -0700570 SkASSERT(length <= size);
571 SkASSERT(offset <= size - length);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000572 if (length > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000573 SkString tmp(size - length);
574 char* dst = tmp.writable_str();
575 const char* src = this->c_str();
576
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000577 if (offset) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000578 memcpy(dst, src, offset);
579 }
bungemandfb9bc42014-08-11 07:19:56 -0700580 size_t tail = size - (offset + length);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000581 if (tail) {
bungemandfb9bc42014-08-11 07:19:56 -0700582 memcpy(dst + offset, src + (offset + length), tail);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000583 }
584 SkASSERT(dst[tmp.size()] == 0);
585 this->swap(tmp);
586 }
587 }
588}
589
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000590void SkString::swap(SkString& other) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000591 this->validate();
592 other.validate();
593
Ben Wagnereccda1c2017-10-05 10:13:51 -0400594 SkTSwap(fRec, other.fRec);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000595}
596
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000597///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000598
tomhudson@google.com3a1f6a02011-06-30 14:39:52 +0000599SkString SkStringPrintf(const char* format, ...) {
600 SkString formattedOutput;
halcanaryd51bdae2016-04-25 09:25:35 -0700601 V_SKSTRING_PRINTF(formattedOutput, format);
tomhudson@google.com3a1f6a02011-06-30 14:39:52 +0000602 return formattedOutput;
603}
604
kkinnunen3e980c32015-12-23 01:33:00 -0800605void SkStrSplit(const char* str, const char* delimiters, SkStrSplitMode splitMode,
606 SkTArray<SkString>* out) {
607 if (splitMode == kCoalesce_SkStrSplitMode) {
rmistry0f515bd2015-12-22 10:22:26 -0800608 // Skip any delimiters.
609 str += strspn(str, delimiters);
kkinnunen9ebc3f02015-12-21 23:48:13 -0800610 }
kkinnunen3e980c32015-12-23 01:33:00 -0800611 if (!*str) {
612 return;
613 }
614
615 while (true) {
616 // Find a token.
617 const size_t len = strcspn(str, delimiters);
618 if (splitMode == kStrict_SkStrSplitMode || len > 0) {
619 out->push_back().set(str, len);
620 str += len;
621 }
622
623 if (!*str) {
624 return;
625 }
626 if (splitMode == kCoalesce_SkStrSplitMode) {
627 // Skip any delimiters.
628 str += strspn(str, delimiters);
629 } else {
630 // Skip one delimiter.
631 str += 1;
632 }
633 }
rmistry@google.comd6bab022013-12-02 13:50:38 +0000634}