blob: a9c1d827f9e61a02c72f1fa8061e0e1f300c7742 [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"
Hal Canaryfdcfb8b2018-06-13 09:42:32 -040010#include "SkString.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000011#include "SkUtils.h"
Mike Klein79aea6a2018-06-11 10:45:26 -040012#include <cstdio>
13#include <new>
14#include <stdarg.h>
reed@google.comfa06e522011-02-28 21:29:58 +000015
16// number of bytes (on the stack) to receive the printf result
bsalomon@google.com77cf4602013-04-22 21:05:48 +000017static const size_t kBufferSize = 1024;
reed@google.comfa06e522011-02-28 21:29:58 +000018
Brian Salomon43227522018-04-25 12:58:23 -040019static const char* apply_format_string(const char* format, va_list args, char* stackBuffer,
20 size_t stackBufferSize, int* length, SkString* heapBuffer) {
21 va_list argsCopy;
22 va_copy(argsCopy, args);
23 *length = std::vsnprintf(stackBuffer, stackBufferSize, format, args);
24 if (*length < 0) {
25 SkDebugf("SkString: vsnprintf reported error.");
26 va_end(argsCopy);
27 *length = 0;
28 return stackBuffer;
29 }
30 if (*length < SkToInt(stackBufferSize)) {
31 va_end(argsCopy);
32 return stackBuffer;
33 }
34 heapBuffer->resize(*length);
35 SkDEBUGCODE(int check =)
36 std::vsnprintf(heapBuffer->writable_str(), *length + 1, format, argsCopy);
37 SkASSERT(check == *length);
38 va_end(argsCopy);
39 return heapBuffer->c_str();
40}
41
42#define ARGS_TO_BUFFER(format, buffer, size, written, result) \
43 SkString overflow; \
44 do { \
45 va_list args; \
46 va_start(args, format); \
47 result = apply_format_string(format, args, buffer, size, &written, &overflow); \
48 va_end(args); \
reed@google.comfa06e522011-02-28 21:29:58 +000049 } while (0)
50
Brian Salomon43227522018-04-25 12:58:23 -040051#define V_SKSTRING_PRINTF(output, format) \
52 do { \
53 char buffer[kBufferSize]; \
54 va_list args; \
55 va_start(args, format); \
56 int length; \
57 auto result = apply_format_string(format, args, buffer, kBufferSize, &length, &output); \
58 SkASSERT(result == output.c_str() || result == buffer); \
59 if (result == buffer) { \
60 output.set(buffer, length); \
61 } \
62 } while (0)
halcanaryd51bdae2016-04-25 09:25:35 -070063
reed@google.comfa06e522011-02-28 21:29:58 +000064///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +000065
epoger@google.come8ebeb12012-10-29 16:42:11 +000066bool SkStrEndsWith(const char string[], const char suffixStr[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000067 SkASSERT(string);
epoger@google.come8ebeb12012-10-29 16:42:11 +000068 SkASSERT(suffixStr);
reed@android.com8a1c16f2008-12-17 15:59:43 +000069 size_t strLen = strlen(string);
epoger@google.come8ebeb12012-10-29 16:42:11 +000070 size_t suffixLen = strlen(suffixStr);
reed@android.com8a1c16f2008-12-17 15:59:43 +000071 return strLen >= suffixLen &&
epoger@google.come8ebeb12012-10-29 16:42:11 +000072 !strncmp(string + strLen - suffixLen, suffixStr, suffixLen);
73}
74
75bool SkStrEndsWith(const char string[], const char suffixChar) {
76 SkASSERT(string);
77 size_t strLen = strlen(string);
78 if (0 == strLen) {
79 return false;
80 } else {
81 return (suffixChar == string[strLen-1]);
82 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000083}
84
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +000085int SkStrStartsWithOneOf(const char string[], const char prefixes[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000086 int index = 0;
87 do {
88 const char* limit = strchr(prefixes, '\0');
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +000089 if (!strncmp(string, prefixes, limit - prefixes)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000090 return index;
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +000091 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000092 prefixes = limit + 1;
93 index++;
94 } while (prefixes[0]);
95 return -1;
96}
97
epoger@google.comd88a3d82013-06-19 18:27:20 +000098char* SkStrAppendU32(char string[], uint32_t dec) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000099 SkDEBUGCODE(char* start = string;)
100
epoger@google.comd88a3d82013-06-19 18:27:20 +0000101 char buffer[SkStrAppendU32_MaxSize];
reed@android.com8a1c16f2008-12-17 15:59:43 +0000102 char* p = buffer + sizeof(buffer);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000103
reed@android.com8a1c16f2008-12-17 15:59:43 +0000104 do {
105 *--p = SkToU8('0' + dec % 10);
106 dec /= 10;
107 } while (dec != 0);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000108
reed@android.com8a1c16f2008-12-17 15:59:43 +0000109 SkASSERT(p >= buffer);
110 char* stop = buffer + sizeof(buffer);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000111 while (p < stop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000112 *string++ = *p++;
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000113 }
epoger@google.comd88a3d82013-06-19 18:27:20 +0000114 SkASSERT(string - start <= SkStrAppendU32_MaxSize);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000115 return string;
116}
117
epoger@google.comd88a3d82013-06-19 18:27:20 +0000118char* SkStrAppendS32(char string[], int32_t dec) {
mtklein225fb982014-11-05 11:35:21 -0800119 uint32_t udec = dec;
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +0000120 if (dec < 0) {
epoger@google.comd88a3d82013-06-19 18:27:20 +0000121 *string++ = '-';
mtklein225fb982014-11-05 11:35:21 -0800122 udec = ~udec + 1; // udec = -udec, but silences some warnings that are trying to be helpful
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +0000123 }
mtklein225fb982014-11-05 11:35:21 -0800124 return SkStrAppendU32(string, udec);
epoger@google.comd88a3d82013-06-19 18:27:20 +0000125}
126
127char* SkStrAppendU64(char string[], uint64_t dec, int minDigits) {
128 SkDEBUGCODE(char* start = string;)
129
130 char buffer[SkStrAppendU64_MaxSize];
131 char* p = buffer + sizeof(buffer);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000132
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +0000133 do {
caryclark@google.com803eceb2012-06-06 12:09:34 +0000134 *--p = SkToU8('0' + (int32_t) (dec % 10));
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +0000135 dec /= 10;
136 minDigits--;
137 } while (dec != 0);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000138
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +0000139 while (minDigits > 0) {
140 *--p = '0';
141 minDigits--;
142 }
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +0000143
144 SkASSERT(p >= buffer);
145 size_t cp_len = buffer + sizeof(buffer) - p;
146 memcpy(string, p, cp_len);
147 string += cp_len;
148
epoger@google.comd88a3d82013-06-19 18:27:20 +0000149 SkASSERT(string - start <= SkStrAppendU64_MaxSize);
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +0000150 return string;
151}
152
epoger@google.comd88a3d82013-06-19 18:27:20 +0000153char* SkStrAppendS64(char string[], int64_t dec, int minDigits) {
mtklein225fb982014-11-05 11:35:21 -0800154 uint64_t udec = dec;
epoger@google.comd88a3d82013-06-19 18:27:20 +0000155 if (dec < 0) {
156 *string++ = '-';
mtklein225fb982014-11-05 11:35:21 -0800157 udec = ~udec + 1; // udec = -udec, but silences some warnings that are trying to be helpful
epoger@google.comd88a3d82013-06-19 18:27:20 +0000158 }
mtklein225fb982014-11-05 11:35:21 -0800159 return SkStrAppendU64(string, udec, minDigits);
epoger@google.comd88a3d82013-06-19 18:27:20 +0000160}
161
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000162char* SkStrAppendFloat(char string[], float value) {
reed@google.com8072e4f2011-03-01 15:44:08 +0000163 // since floats have at most 8 significant digits, we limit our %g to that.
164 static const char gFormat[] = "%.8g";
165 // make it 1 larger for the terminating 0
166 char buffer[SkStrAppendScalar_MaxSize + 1];
Hal Canary09f818d2018-02-22 15:12:46 -0500167 int len = snprintf(buffer, sizeof(buffer), gFormat, value);
reed@google.com8072e4f2011-03-01 15:44:08 +0000168 memcpy(string, buffer, len);
vandebo@chromium.org677cbed2011-03-03 18:20:12 +0000169 SkASSERT(len <= SkStrAppendScalar_MaxSize);
reed@google.com8072e4f2011-03-01 15:44:08 +0000170 return string + len;
vandebo@chromium.org677cbed2011-03-03 18:20:12 +0000171}
reed@android.com8a1c16f2008-12-17 15:59:43 +0000172
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000173///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000174
Ben Wagnereccda1c2017-10-05 10:13:51 -0400175const SkString::Rec SkString::gEmptyRec(0, 0);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000176
177#define SizeOfRec() (gEmptyRec.data() - (const char*)&gEmptyRec)
178
reed@google.com8cb10882013-06-04 20:36:52 +0000179static uint32_t trim_size_t_to_u32(size_t value) {
180 if (sizeof(size_t) > sizeof(uint32_t)) {
Hal Canarye1bc7de2018-06-13 15:01:39 -0400181 if (value > UINT32_MAX) {
182 value = UINT32_MAX;
reed@google.com8cb10882013-06-04 20:36:52 +0000183 }
184 }
185 return (uint32_t)value;
186}
187
188static size_t check_add32(size_t base, size_t extra) {
Hal Canarye1bc7de2018-06-13 15:01:39 -0400189 SkASSERT(base <= UINT32_MAX);
reed@google.com8cb10882013-06-04 20:36:52 +0000190 if (sizeof(size_t) > sizeof(uint32_t)) {
Hal Canarye1bc7de2018-06-13 15:01:39 -0400191 if (base + extra > UINT32_MAX) {
192 extra = UINT32_MAX - base;
reed@google.com8cb10882013-06-04 20:36:52 +0000193 }
194 }
195 return extra;
196}
197
Ben Wagnereccda1c2017-10-05 10:13:51 -0400198sk_sp<SkString::Rec> SkString::Rec::Make(const char text[], size_t len) {
reed@google.com4bce1152011-09-14 16:13:58 +0000199 if (0 == len) {
Ben Wagnereccda1c2017-10-05 10:13:51 -0400200 return sk_sp<SkString::Rec>(const_cast<Rec*>(&gEmptyRec));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000201 }
Ben Wagnereccda1c2017-10-05 10:13:51 -0400202
Mike Reed33f38b02018-02-14 13:58:06 -0500203 SkSafeMath safe;
204 // We store a 32bit version of the length
205 uint32_t stringLen = safe.castTo<uint32_t>(len);
206 // Add SizeOfRec() for our overhead and 1 for null-termination
207 size_t allocationSize = safe.add(len, SizeOfRec() + sizeof(char));
208 // Align up to a multiple of 4
209 allocationSize = safe.alignUp(allocationSize, 4);
Ben Wagnereccda1c2017-10-05 10:13:51 -0400210
Mike Reed33f38b02018-02-14 13:58:06 -0500211 SkASSERT_RELEASE(safe.ok());
212
213 void* storage = ::operator new (allocationSize);
214 sk_sp<Rec> rec(new (storage) Rec(stringLen, 1));
Ben Wagnereccda1c2017-10-05 10:13:51 -0400215 if (text) {
216 memcpy(rec->data(), text, len);
217 }
218 rec->data()[len] = 0;
Robert Phillips01f8e412017-10-05 11:28:20 +0000219 return rec;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000220}
221
Ben Wagnereccda1c2017-10-05 10:13:51 -0400222void SkString::Rec::ref() const {
223 if (this == &SkString::gEmptyRec) {
224 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000225 }
Ben Wagnereccda1c2017-10-05 10:13:51 -0400226 SkAssertResult(this->fRefCnt.fetch_add(+1, std::memory_order_relaxed));
227}
228
229void SkString::Rec::unref() const {
230 if (this == &SkString::gEmptyRec) {
231 return;
232 }
233 int32_t oldRefCnt = this->fRefCnt.fetch_add(-1, std::memory_order_acq_rel);
234 SkASSERT(oldRefCnt);
235 if (1 == oldRefCnt) {
236 delete this;
237 }
238}
239
240bool SkString::Rec::unique() const {
241 return fRefCnt.load(std::memory_order_acquire) == 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000242}
243
244#ifdef SK_DEBUG
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000245void SkString::validate() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000246 // make sure know one has written over our global
reed@google.com4bce1152011-09-14 16:13:58 +0000247 SkASSERT(0 == gEmptyRec.fLength);
Ben Wagneraf893662017-10-03 11:08:14 -0400248 SkASSERT(0 == gEmptyRec.fRefCnt.load(std::memory_order_relaxed));
reed@google.com4bce1152011-09-14 16:13:58 +0000249 SkASSERT(0 == gEmptyRec.data()[0]);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000250
Ben Wagnereccda1c2017-10-05 10:13:51 -0400251 if (fRec.get() != &gEmptyRec) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000252 SkASSERT(fRec->fLength > 0);
Ben Wagneraf893662017-10-03 11:08:14 -0400253 SkASSERT(fRec->fRefCnt.load(std::memory_order_relaxed) > 0);
reed@google.com4bce1152011-09-14 16:13:58 +0000254 SkASSERT(0 == fRec->data()[fRec->fLength]);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000255 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000256}
257#endif
258
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000259///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000260
261SkString::SkString() : fRec(const_cast<Rec*>(&gEmptyRec)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000262}
263
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000264SkString::SkString(size_t len) {
Ben Wagnereccda1c2017-10-05 10:13:51 -0400265 fRec = Rec::Make(nullptr, len);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000266}
267
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000268SkString::SkString(const char text[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000269 size_t len = text ? strlen(text) : 0;
270
Ben Wagnereccda1c2017-10-05 10:13:51 -0400271 fRec = Rec::Make(text, len);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000272}
273
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000274SkString::SkString(const char text[], size_t len) {
Ben Wagnereccda1c2017-10-05 10:13:51 -0400275 fRec = Rec::Make(text, len);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000276}
277
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000278SkString::SkString(const SkString& src) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000279 src.validate();
280
Ben Wagnereccda1c2017-10-05 10:13:51 -0400281 fRec = src.fRec;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000282}
283
bungeman9d552972016-02-07 18:42:54 -0800284SkString::SkString(SkString&& src) {
285 src.validate();
286
Ben Wagnereccda1c2017-10-05 10:13:51 -0400287 fRec = std::move(src.fRec);
288 src.fRec.reset(const_cast<Rec*>(&gEmptyRec));
bungeman9d552972016-02-07 18:42:54 -0800289}
290
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000291SkString::~SkString() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000292 this->validate();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000293}
294
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000295bool SkString::equals(const SkString& src) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000296 return fRec == src.fRec || this->equals(src.c_str(), src.size());
297}
298
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000299bool SkString::equals(const char text[]) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000300 return this->equals(text, text ? strlen(text) : 0);
301}
302
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000303bool SkString::equals(const char text[], size_t len) const {
halcanary96fcdcc2015-08-27 07:41:13 -0700304 SkASSERT(len == 0 || text != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000305
306 return fRec->fLength == len && !memcmp(fRec->data(), text, len);
307}
308
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000309SkString& SkString::operator=(const SkString& src) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000310 this->validate();
311
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000312 if (fRec != src.fRec) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000313 SkString tmp(src);
314 this->swap(tmp);
315 }
316 return *this;
317}
318
bungeman9d552972016-02-07 18:42:54 -0800319SkString& SkString::operator=(SkString&& src) {
320 this->validate();
321
322 if (fRec != src.fRec) {
323 this->swap(src);
324 }
325 return *this;
326}
327
bsalomon@google.comfc296292011-05-06 13:53:47 +0000328SkString& SkString::operator=(const char text[]) {
329 this->validate();
330
331 SkString tmp(text);
332 this->swap(tmp);
333
334 return *this;
335}
336
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000337void SkString::reset() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000338 this->validate();
Ben Wagnereccda1c2017-10-05 10:13:51 -0400339 fRec.reset(const_cast<Rec*>(&gEmptyRec));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000340}
341
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000342char* SkString::writable_str() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000343 this->validate();
344
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000345 if (fRec->fLength) {
Ben Wagneraf893662017-10-03 11:08:14 -0400346 if (!fRec->unique()) {
Ben Wagnereccda1c2017-10-05 10:13:51 -0400347 fRec = Rec::Make(fRec->data(), fRec->fLength);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000348 }
349 }
350 return fRec->data();
351}
352
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000353void SkString::set(const char text[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000354 this->set(text, text ? strlen(text) : 0);
355}
356
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000357void SkString::set(const char text[], size_t len) {
reed@google.com8cb10882013-06-04 20:36:52 +0000358 len = trim_size_t_to_u32(len);
Ben Wagneraf893662017-10-03 11:08:14 -0400359 bool unique = fRec->unique();
reed@google.com4bce1152011-09-14 16:13:58 +0000360 if (0 == len) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000361 this->reset();
Ben Wagneraf893662017-10-03 11:08:14 -0400362 } else if (unique && len <= fRec->fLength) {
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000363 // should we resize if len <<<< fLength, to save RAM? (e.g. len < (fLength>>1))?
reed@android.com8a1c16f2008-12-17 15:59:43 +0000364 // just use less of the buffer without allocating a smaller one
365 char* p = this->writable_str();
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000366 if (text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000367 memcpy(p, text, len);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000368 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000369 p[len] = 0;
reed@google.com8cb10882013-06-04 20:36:52 +0000370 fRec->fLength = SkToU32(len);
Ben Wagneraf893662017-10-03 11:08:14 -0400371 } else if (unique && (fRec->fLength >> 2) == (len >> 2)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000372 // we have spare room in the current allocation, so don't alloc a larger one
373 char* p = this->writable_str();
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000374 if (text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000375 memcpy(p, text, len);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000376 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000377 p[len] = 0;
reed@google.com8cb10882013-06-04 20:36:52 +0000378 fRec->fLength = SkToU32(len);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000379 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000380 SkString tmp(text, len);
381 this->swap(tmp);
382 }
383}
384
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000385void SkString::insert(size_t offset, const char text[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000386 this->insert(offset, text, text ? strlen(text) : 0);
387}
388
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000389void SkString::insert(size_t offset, const char text[], size_t len) {
390 if (len) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000391 size_t length = fRec->fLength;
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000392 if (offset > length) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000393 offset = length;
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000394 }
skia.committer@gmail.com8f6ef402013-06-05 07:01:06 +0000395
reed@google.com8cb10882013-06-04 20:36:52 +0000396 // Check if length + len exceeds 32bits, we trim len
397 len = check_add32(length, len);
398 if (0 == len) {
399 return;
400 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000401
402 /* If we're the only owner, and we have room in our allocation for the insert,
403 do it in place, rather than allocating a new buffer.
404
405 To know we have room, compare the allocated sizes
406 beforeAlloc = SkAlign4(length + 1)
407 afterAlloc = SkAligh4(length + 1 + len)
408 but SkAlign4(x) is (x + 3) >> 2 << 2
409 which is equivalent for testing to (length + 1 + 3) >> 2 == (length + 1 + 3 + len) >> 2
410 and we can then eliminate the +1+3 since that doesn't affec the answer
411 */
Ben Wagneraf893662017-10-03 11:08:14 -0400412 if (fRec->unique() && (length >> 2) == ((length + len) >> 2)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000413 char* dst = this->writable_str();
414
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000415 if (offset < length) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000416 memmove(dst + offset + len, dst + offset, length - offset);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000417 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000418 memcpy(dst + offset, text, len);
419
420 dst[length + len] = 0;
reed@google.com8cb10882013-06-04 20:36:52 +0000421 fRec->fLength = SkToU32(length + len);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000422 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000423 /* Seems we should use realloc here, since that is safe if it fails
424 (we have the original data), and might be faster than alloc/copy/free.
425 */
426 SkString tmp(fRec->fLength + len);
427 char* dst = tmp.writable_str();
428
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000429 if (offset > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000430 memcpy(dst, fRec->data(), offset);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000431 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000432 memcpy(dst + offset, text, len);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000433 if (offset < fRec->fLength) {
434 memcpy(dst + offset + len, fRec->data() + offset,
435 fRec->fLength - offset);
436 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000437
438 this->swap(tmp);
439 }
440 }
441}
442
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000443void SkString::insertUnichar(size_t offset, SkUnichar uni) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000444 char buffer[kMaxBytesInUTF8Sequence];
445 size_t len = SkUTF8_FromUnichar(uni, buffer);
446
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000447 if (len) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000448 this->insert(offset, buffer, len);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000449 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000450}
451
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000452void SkString::insertS32(size_t offset, int32_t dec) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000453 char buffer[SkStrAppendS32_MaxSize];
454 char* stop = SkStrAppendS32(buffer, dec);
455 this->insert(offset, buffer, stop - buffer);
456}
457
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000458void SkString::insertS64(size_t offset, int64_t dec, int minDigits) {
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +0000459 char buffer[SkStrAppendS64_MaxSize];
460 char* stop = SkStrAppendS64(buffer, dec, minDigits);
461 this->insert(offset, buffer, stop - buffer);
462}
463
epoger@google.comd88a3d82013-06-19 18:27:20 +0000464void SkString::insertU32(size_t offset, uint32_t dec) {
465 char buffer[SkStrAppendU32_MaxSize];
466 char* stop = SkStrAppendU32(buffer, dec);
467 this->insert(offset, buffer, stop - buffer);
468}
469
470void SkString::insertU64(size_t offset, uint64_t dec, int minDigits) {
471 char buffer[SkStrAppendU64_MaxSize];
472 char* stop = SkStrAppendU64(buffer, dec, minDigits);
473 this->insert(offset, buffer, stop - buffer);
474}
475
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000476void SkString::insertHex(size_t offset, uint32_t hex, int minDigits) {
bungeman62ce0302015-08-28 09:09:32 -0700477 minDigits = SkTPin(minDigits, 0, 8);
reed@google.comfa06e522011-02-28 21:29:58 +0000478
reed@android.com8a1c16f2008-12-17 15:59:43 +0000479 char buffer[8];
480 char* p = buffer + sizeof(buffer);
481
482 do {
Hal Canaryd6e6e662017-06-17 10:38:13 -0400483 *--p = SkHexadecimalDigits::gUpper[hex & 0xF];
reed@android.com8a1c16f2008-12-17 15:59:43 +0000484 hex >>= 4;
485 minDigits -= 1;
486 } while (hex != 0);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000487
488 while (--minDigits >= 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000489 *--p = '0';
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000490 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000491
492 SkASSERT(p >= buffer);
493 this->insert(offset, p, buffer + sizeof(buffer) - p);
494}
495
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000496void SkString::insertScalar(size_t offset, SkScalar value) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000497 char buffer[SkStrAppendScalar_MaxSize];
498 char* stop = SkStrAppendScalar(buffer, value);
499 this->insert(offset, buffer, stop - buffer);
500}
501
reed@android.com8a1c16f2008-12-17 15:59:43 +0000502void SkString::printf(const char format[], ...) {
halcanaryd51bdae2016-04-25 09:25:35 -0700503 V_SKSTRING_PRINTF((*this), format);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000504}
505
506void SkString::appendf(const char format[], ...) {
Brian Salomon43227522018-04-25 12:58:23 -0400507 char buffer[kBufferSize];
joshualitt976386b2014-10-23 18:23:32 -0700508 int length;
Brian Salomon43227522018-04-25 12:58:23 -0400509 const char* result;
510 ARGS_TO_BUFFER(format, buffer, kBufferSize, length, result);
reed@google.comfa06e522011-02-28 21:29:58 +0000511
Brian Salomon43227522018-04-25 12:58:23 -0400512 this->append(result, length);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000513}
514
commit-bot@chromium.orgce0e4ef2013-11-21 17:20:17 +0000515void SkString::appendVAList(const char format[], va_list args) {
Brian Salomon43227522018-04-25 12:58:23 -0400516 char buffer[kBufferSize];
Hal Canary09f818d2018-02-22 15:12:46 -0500517 int length = vsnprintf(buffer, kBufferSize, format, args);
joshualitt976386b2014-10-23 18:23:32 -0700518 SkASSERT(length >= 0 && length < SkToInt(kBufferSize));
bsalomon@google.comf910d3b2013-03-07 17:06:57 +0000519
joshualitt976386b2014-10-23 18:23:32 -0700520 this->append(buffer, length);
bsalomon@google.comf910d3b2013-03-07 17:06:57 +0000521}
522
reed@android.com8a1c16f2008-12-17 15:59:43 +0000523void SkString::prependf(const char format[], ...) {
Brian Salomon43227522018-04-25 12:58:23 -0400524 char buffer[kBufferSize];
joshualitt976386b2014-10-23 18:23:32 -0700525 int length;
Brian Salomon43227522018-04-25 12:58:23 -0400526 const char* result;
527 ARGS_TO_BUFFER(format, buffer, kBufferSize, length, result);
reed@google.comfa06e522011-02-28 21:29:58 +0000528
Brian Salomon43227522018-04-25 12:58:23 -0400529 this->prepend(result, length);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000530}
531
joshualitt30ba4362014-08-21 20:18:45 -0700532void SkString::prependVAList(const char format[], va_list args) {
Brian Salomon43227522018-04-25 12:58:23 -0400533 char buffer[kBufferSize];
Hal Canary09f818d2018-02-22 15:12:46 -0500534 int length = vsnprintf(buffer, kBufferSize, format, args);
joshualitt976386b2014-10-23 18:23:32 -0700535 SkASSERT(length >= 0 && length < SkToInt(kBufferSize));
joshualitt30ba4362014-08-21 20:18:45 -0700536
joshualitt976386b2014-10-23 18:23:32 -0700537 this->prepend(buffer, length);
joshualitt30ba4362014-08-21 20:18:45 -0700538}
539
540
reed@google.comfa06e522011-02-28 21:29:58 +0000541///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000542
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000543void SkString::remove(size_t offset, size_t length) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000544 size_t size = this->size();
545
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000546 if (offset < size) {
bungemandfb9bc42014-08-11 07:19:56 -0700547 if (length > size - offset) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000548 length = size - offset;
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000549 }
bungemandfb9bc42014-08-11 07:19:56 -0700550 SkASSERT(length <= size);
551 SkASSERT(offset <= size - length);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000552 if (length > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000553 SkString tmp(size - length);
554 char* dst = tmp.writable_str();
555 const char* src = this->c_str();
556
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000557 if (offset) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000558 memcpy(dst, src, offset);
559 }
bungemandfb9bc42014-08-11 07:19:56 -0700560 size_t tail = size - (offset + length);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000561 if (tail) {
bungemandfb9bc42014-08-11 07:19:56 -0700562 memcpy(dst + offset, src + (offset + length), tail);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000563 }
564 SkASSERT(dst[tmp.size()] == 0);
565 this->swap(tmp);
566 }
567 }
568}
569
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000570void SkString::swap(SkString& other) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000571 this->validate();
572 other.validate();
573
Ben Wagnereccda1c2017-10-05 10:13:51 -0400574 SkTSwap(fRec, other.fRec);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000575}
576
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000577///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000578
tomhudson@google.com3a1f6a02011-06-30 14:39:52 +0000579SkString SkStringPrintf(const char* format, ...) {
580 SkString formattedOutput;
halcanaryd51bdae2016-04-25 09:25:35 -0700581 V_SKSTRING_PRINTF(formattedOutput, format);
tomhudson@google.com3a1f6a02011-06-30 14:39:52 +0000582 return formattedOutput;
583}
584
kkinnunen3e980c32015-12-23 01:33:00 -0800585void SkStrSplit(const char* str, const char* delimiters, SkStrSplitMode splitMode,
586 SkTArray<SkString>* out) {
587 if (splitMode == kCoalesce_SkStrSplitMode) {
rmistry0f515bd2015-12-22 10:22:26 -0800588 // Skip any delimiters.
589 str += strspn(str, delimiters);
kkinnunen9ebc3f02015-12-21 23:48:13 -0800590 }
kkinnunen3e980c32015-12-23 01:33:00 -0800591 if (!*str) {
592 return;
593 }
594
595 while (true) {
596 // Find a token.
597 const size_t len = strcspn(str, delimiters);
598 if (splitMode == kStrict_SkStrSplitMode || len > 0) {
599 out->push_back().set(str, len);
600 str += len;
601 }
602
603 if (!*str) {
604 return;
605 }
606 if (splitMode == kCoalesce_SkStrSplitMode) {
607 // Skip any delimiters.
608 str += strspn(str, delimiters);
609 } else {
610 // Skip one delimiter.
611 str += 1;
612 }
613 }
rmistry@google.comd6bab022013-12-02 13:50:38 +0000614}