blob: 85fecc9e048f7407da67ba863cd0c97c0fc5a822 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
2/*
3 * Copyright 2006 The Android Open Source Project
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
reed@android.com8a1c16f2008-12-17 15:59:43 +00009
10#include "SkString.h"
11#include "SkFixed.h"
reed@google.com4bce1152011-09-14 16:13:58 +000012#include "SkThread.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000013#include "SkUtils.h"
14#include <stdarg.h>
reed@google.comfa06e522011-02-28 21:29:58 +000015#include <stdio.h>
16
17// number of bytes (on the stack) to receive the printf result
18static const size_t kBufferSize = 256;
19
20#ifdef SK_BUILD_FOR_WIN
tomhudson@google.com47e0a092011-07-08 17:49:22 +000021 #define VSNPRINTF(buffer, size, format, args) \
22 _vsnprintf_s(buffer, size, _TRUNCATE, format, args)
reed@google.comfa06e522011-02-28 21:29:58 +000023 #define SNPRINTF _snprintf
24#else
25 #define VSNPRINTF vsnprintf
26 #define SNPRINTF snprintf
27#endif
28
29#define ARGS_TO_BUFFER(format, buffer, size) \
30 do { \
31 va_list args; \
32 va_start(args, format); \
33 VSNPRINTF(buffer, size, format, args); \
34 va_end(args); \
35 } while (0)
36
37///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +000038
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +000039bool SkStrEndsWith(const char string[], const char suffix[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000040 SkASSERT(string);
41 SkASSERT(suffix);
42 size_t strLen = strlen(string);
43 size_t suffixLen = strlen(suffix);
44 return strLen >= suffixLen &&
45 !strncmp(string + strLen - suffixLen, suffix, suffixLen);
46}
47
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +000048int SkStrStartsWithOneOf(const char string[], const char prefixes[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000049 int index = 0;
50 do {
51 const char* limit = strchr(prefixes, '\0');
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +000052 if (!strncmp(string, prefixes, limit - prefixes)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000053 return index;
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +000054 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000055 prefixes = limit + 1;
56 index++;
57 } while (prefixes[0]);
58 return -1;
59}
60
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +000061char* SkStrAppendS32(char string[], int32_t dec) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000062 SkDEBUGCODE(char* start = string;)
63
64 char buffer[SkStrAppendS32_MaxSize];
65 char* p = buffer + sizeof(buffer);
66 bool neg = false;
67
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +000068 if (dec < 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000069 neg = true;
70 dec = -dec;
71 }
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +000072
reed@android.com8a1c16f2008-12-17 15:59:43 +000073 do {
74 *--p = SkToU8('0' + dec % 10);
75 dec /= 10;
76 } while (dec != 0);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +000077
78 if (neg) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000079 *--p = '-';
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +000080 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000081
82 SkASSERT(p >= buffer);
83 char* stop = buffer + sizeof(buffer);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +000084 while (p < stop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000085 *string++ = *p++;
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +000086 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000087 SkASSERT(string - start <= SkStrAppendS32_MaxSize);
88 return string;
89}
90
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +000091char* SkStrAppendS64(char string[], int64_t dec, int minDigits) {
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +000092 SkDEBUGCODE(char* start = string;)
93
94 char buffer[SkStrAppendS64_MaxSize];
95 char* p = buffer + sizeof(buffer);
96 bool neg = false;
97
98 if (dec < 0) {
99 neg = true;
100 dec = -dec;
101 }
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000102
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +0000103 do {
caryclark@google.com803eceb2012-06-06 12:09:34 +0000104 *--p = SkToU8('0' + (int32_t) (dec % 10));
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +0000105 dec /= 10;
106 minDigits--;
107 } while (dec != 0);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000108
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +0000109 while (minDigits > 0) {
110 *--p = '0';
111 minDigits--;
112 }
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +0000113
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000114 if (neg) {
115 *--p = '-';
116 }
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +0000117 SkASSERT(p >= buffer);
118 size_t cp_len = buffer + sizeof(buffer) - p;
119 memcpy(string, p, cp_len);
120 string += cp_len;
121
122 SkASSERT(string - start <= SkStrAppendS64_MaxSize);
123 return string;
124}
125
vandebo@chromium.org677cbed2011-03-03 18:20:12 +0000126#ifdef SK_CAN_USE_FLOAT
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000127char* SkStrAppendFloat(char string[], float value) {
reed@google.com8072e4f2011-03-01 15:44:08 +0000128 // since floats have at most 8 significant digits, we limit our %g to that.
129 static const char gFormat[] = "%.8g";
130 // make it 1 larger for the terminating 0
131 char buffer[SkStrAppendScalar_MaxSize + 1];
132 int len = SNPRINTF(buffer, sizeof(buffer), gFormat, value);
133 memcpy(string, buffer, len);
vandebo@chromium.org677cbed2011-03-03 18:20:12 +0000134 SkASSERT(len <= SkStrAppendScalar_MaxSize);
reed@google.com8072e4f2011-03-01 15:44:08 +0000135 return string + len;
vandebo@chromium.org677cbed2011-03-03 18:20:12 +0000136}
137#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000138
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000139char* SkStrAppendFixed(char string[], SkFixed x) {
vandebo@chromium.org677cbed2011-03-03 18:20:12 +0000140 SkDEBUGCODE(char* start = string;)
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000141 if (x < 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000142 *string++ = '-';
143 x = -x;
144 }
145
146 unsigned frac = x & 0xFFFF;
147 x >>= 16;
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000148 if (frac == 0xFFFF) {
149 // need to do this to "round up", since 65535/65536 is closer to 1 than to .9999
reed@android.com8a1c16f2008-12-17 15:59:43 +0000150 x += 1;
151 frac = 0;
152 }
153 string = SkStrAppendS32(string, x);
154
155 // now handle the fractional part (if any)
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000156 if (frac) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000157 static const uint16_t gTens[] = { 1000, 100, 10, 1 };
158 const uint16_t* tens = gTens;
159
160 x = SkFixedRound(frac * 10000);
161 SkASSERT(x <= 10000);
162 if (x == 10000) {
163 x -= 1;
164 }
165 *string++ = '.';
166 do {
167 unsigned powerOfTen = *tens++;
168 *string++ = SkToU8('0' + x / powerOfTen);
169 x %= powerOfTen;
170 } while (x != 0);
171 }
reed@google.comfa06e522011-02-28 21:29:58 +0000172
reed@android.com8a1c16f2008-12-17 15:59:43 +0000173 SkASSERT(string - start <= SkStrAppendScalar_MaxSize);
174 return string;
175}
176
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000177///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000178
reed@android.com8a1c16f2008-12-17 15:59:43 +0000179// the 3 values are [length] [refcnt] [terminating zero data]
180const SkString::Rec SkString::gEmptyRec = { 0, 0, 0 };
181
182#define SizeOfRec() (gEmptyRec.data() - (const char*)&gEmptyRec)
183
reed@google.com4bce1152011-09-14 16:13:58 +0000184SkString::Rec* SkString::AllocRec(const char text[], size_t len) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000185 Rec* rec;
186
reed@google.com4bce1152011-09-14 16:13:58 +0000187 if (0 == len) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000188 rec = const_cast<Rec*>(&gEmptyRec);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000189 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000190 // add 1 for terminating 0, then align4 so we can have some slop when growing the string
191 rec = (Rec*)sk_malloc_throw(SizeOfRec() + SkAlign4(len + 1));
reed@google.com4bce1152011-09-14 16:13:58 +0000192 rec->fLength = len;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000193 rec->fRefCnt = 1;
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000194 if (text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000195 memcpy(rec->data(), text, len);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000196 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000197 rec->data()[len] = 0;
198 }
199 return rec;
200}
201
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000202SkString::Rec* SkString::RefRec(Rec* src) {
203 if (src != &gEmptyRec) {
reed@google.com4bce1152011-09-14 16:13:58 +0000204 sk_atomic_inc(&src->fRefCnt);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000205 }
206 return src;
207}
208
209#ifdef SK_DEBUG
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000210void SkString::validate() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000211 // make sure know one has written over our global
reed@google.com4bce1152011-09-14 16:13:58 +0000212 SkASSERT(0 == gEmptyRec.fLength);
213 SkASSERT(0 == gEmptyRec.fRefCnt);
214 SkASSERT(0 == gEmptyRec.data()[0]);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000215
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000216 if (fRec != &gEmptyRec) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000217 SkASSERT(fRec->fLength > 0);
218 SkASSERT(fRec->fRefCnt > 0);
reed@google.com4bce1152011-09-14 16:13:58 +0000219 SkASSERT(0 == fRec->data()[fRec->fLength]);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000220 }
221 SkASSERT(fStr == c_str());
222}
223#endif
224
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000225///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000226
227SkString::SkString() : fRec(const_cast<Rec*>(&gEmptyRec)) {
228#ifdef SK_DEBUG
229 fStr = fRec->data();
230#endif
231}
232
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000233SkString::SkString(size_t len) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000234 SkASSERT(SkToU16(len) == len); // can't handle larger than 64K
235
236 fRec = AllocRec(NULL, (U16CPU)len);
237#ifdef SK_DEBUG
238 fStr = fRec->data();
239#endif
240}
241
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000242SkString::SkString(const char text[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000243 size_t len = text ? strlen(text) : 0;
244
245 fRec = AllocRec(text, (U16CPU)len);
246#ifdef SK_DEBUG
247 fStr = fRec->data();
248#endif
249}
250
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000251SkString::SkString(const char text[], size_t len) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000252 fRec = AllocRec(text, (U16CPU)len);
253#ifdef SK_DEBUG
254 fStr = fRec->data();
255#endif
256}
257
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000258SkString::SkString(const SkString& src) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000259 src.validate();
260
261 fRec = RefRec(src.fRec);
262#ifdef SK_DEBUG
263 fStr = fRec->data();
264#endif
265}
266
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000267SkString::~SkString() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000268 this->validate();
269
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000270 if (fRec->fLength) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000271 SkASSERT(fRec->fRefCnt > 0);
reed@google.com4bce1152011-09-14 16:13:58 +0000272 if (sk_atomic_dec(&fRec->fRefCnt) == 1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000273 sk_free(fRec);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000274 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000275 }
276}
277
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000278bool SkString::equals(const SkString& src) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000279 return fRec == src.fRec || this->equals(src.c_str(), src.size());
280}
281
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000282bool SkString::equals(const char text[]) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000283 return this->equals(text, text ? strlen(text) : 0);
284}
285
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000286bool SkString::equals(const char text[], size_t len) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000287 SkASSERT(len == 0 || text != NULL);
288
289 return fRec->fLength == len && !memcmp(fRec->data(), text, len);
290}
291
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000292SkString& SkString::operator=(const SkString& src) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000293 this->validate();
294
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000295 if (fRec != src.fRec) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000296 SkString tmp(src);
297 this->swap(tmp);
298 }
299 return *this;
300}
301
bsalomon@google.comfc296292011-05-06 13:53:47 +0000302SkString& SkString::operator=(const char text[]) {
303 this->validate();
304
305 SkString tmp(text);
306 this->swap(tmp);
307
308 return *this;
309}
310
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000311void SkString::reset() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000312 this->validate();
313
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000314 if (fRec->fLength) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000315 SkASSERT(fRec->fRefCnt > 0);
reed@google.com4bce1152011-09-14 16:13:58 +0000316 if (sk_atomic_dec(&fRec->fRefCnt) == 1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000317 sk_free(fRec);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000318 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000319 }
320
321 fRec = const_cast<Rec*>(&gEmptyRec);
322#ifdef SK_DEBUG
323 fStr = fRec->data();
324#endif
325}
326
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000327char* SkString::writable_str() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000328 this->validate();
329
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000330 if (fRec->fLength) {
331 if (fRec->fRefCnt > 1) {
reed@google.com4bce1152011-09-14 16:13:58 +0000332 Rec* rec = AllocRec(fRec->data(), fRec->fLength);
333 if (sk_atomic_dec(&fRec->fRefCnt) == 1) {
334 // In this case after our check of fRecCnt > 1, we suddenly
335 // did become the only owner, so now we have two copies of the
336 // data (fRec and rec), so we need to delete one of them.
337 sk_free(fRec);
338 }
339 fRec = rec;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000340 #ifdef SK_DEBUG
341 fStr = fRec->data();
342 #endif
343 }
344 }
345 return fRec->data();
346}
347
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000348void SkString::set(const char text[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000349 this->set(text, text ? strlen(text) : 0);
350}
351
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000352void SkString::set(const char text[], size_t len) {
reed@google.com4bce1152011-09-14 16:13:58 +0000353 if (0 == len) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000354 this->reset();
reed@google.com4bce1152011-09-14 16:13:58 +0000355 } else if (1 == fRec->fRefCnt && len <= fRec->fLength) {
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000356 // should we resize if len <<<< fLength, to save RAM? (e.g. len < (fLength>>1))?
reed@android.com8a1c16f2008-12-17 15:59:43 +0000357 // just use less of the buffer without allocating a smaller one
358 char* p = this->writable_str();
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000359 if (text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000360 memcpy(p, text, len);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000361 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000362 p[len] = 0;
reed@google.com4bce1152011-09-14 16:13:58 +0000363 fRec->fLength = len;
364 } else if (1 == fRec->fRefCnt && (fRec->fLength >> 2) == (len >> 2)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000365 // we have spare room in the current allocation, so don't alloc a larger one
366 char* p = this->writable_str();
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000367 if (text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000368 memcpy(p, text, len);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000369 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000370 p[len] = 0;
reed@google.com4bce1152011-09-14 16:13:58 +0000371 fRec->fLength = len;
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000372 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000373 SkString tmp(text, len);
374 this->swap(tmp);
375 }
376}
377
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000378void SkString::setUTF16(const uint16_t src[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000379 int count = 0;
380
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000381 while (src[count]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000382 count += 1;
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000383 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000384 setUTF16(src, count);
385}
386
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000387void SkString::setUTF16(const uint16_t src[], size_t count) {
reed@google.com4bce1152011-09-14 16:13:58 +0000388 if (0 == count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000389 this->reset();
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000390 } else if (count <= fRec->fLength) {
391 // should we resize if len <<<< fLength, to save RAM? (e.g. len < (fLength>>1))
392 if (count < fRec->fLength) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000393 this->resize(count);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000394 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000395 char* p = this->writable_str();
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000396 for (size_t i = 0; i < count; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000397 p[i] = SkToU8(src[i]);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000398 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000399 p[count] = 0;
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000400 } else {
401 SkString tmp(count); // puts a null terminator at the end of the string
402 char* p = tmp.writable_str();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000403
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000404 for (size_t i = 0; i < count; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000405 p[i] = SkToU8(src[i]);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000406 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000407 this->swap(tmp);
408 }
409}
410
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000411void SkString::insert(size_t offset, const char text[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000412 this->insert(offset, text, text ? strlen(text) : 0);
413}
414
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000415void SkString::insert(size_t offset, const char text[], size_t len) {
416 if (len) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000417 size_t length = fRec->fLength;
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000418 if (offset > length) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000419 offset = length;
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000420 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000421
422 /* If we're the only owner, and we have room in our allocation for the insert,
423 do it in place, rather than allocating a new buffer.
424
425 To know we have room, compare the allocated sizes
426 beforeAlloc = SkAlign4(length + 1)
427 afterAlloc = SkAligh4(length + 1 + len)
428 but SkAlign4(x) is (x + 3) >> 2 << 2
429 which is equivalent for testing to (length + 1 + 3) >> 2 == (length + 1 + 3 + len) >> 2
430 and we can then eliminate the +1+3 since that doesn't affec the answer
431 */
reed@google.com4bce1152011-09-14 16:13:58 +0000432 if (1 == fRec->fRefCnt && (length >> 2) == ((length + len) >> 2)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000433 char* dst = this->writable_str();
434
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000435 if (offset < length) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000436 memmove(dst + offset + len, dst + offset, length - offset);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000437 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000438 memcpy(dst + offset, text, len);
439
440 dst[length + len] = 0;
reed@google.com4bce1152011-09-14 16:13:58 +0000441 fRec->fLength = length + len;
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000442 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000443 /* Seems we should use realloc here, since that is safe if it fails
444 (we have the original data), and might be faster than alloc/copy/free.
445 */
446 SkString tmp(fRec->fLength + len);
447 char* dst = tmp.writable_str();
448
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000449 if (offset > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000450 memcpy(dst, fRec->data(), offset);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000451 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000452 memcpy(dst + offset, text, len);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000453 if (offset < fRec->fLength) {
454 memcpy(dst + offset + len, fRec->data() + offset,
455 fRec->fLength - offset);
456 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000457
458 this->swap(tmp);
459 }
460 }
461}
462
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000463void SkString::insertUnichar(size_t offset, SkUnichar uni) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000464 char buffer[kMaxBytesInUTF8Sequence];
465 size_t len = SkUTF8_FromUnichar(uni, buffer);
466
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000467 if (len) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000468 this->insert(offset, buffer, len);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000469 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000470}
471
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000472void SkString::insertS32(size_t offset, int32_t dec) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000473 char buffer[SkStrAppendS32_MaxSize];
474 char* stop = SkStrAppendS32(buffer, dec);
475 this->insert(offset, buffer, stop - buffer);
476}
477
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000478void SkString::insertS64(size_t offset, int64_t dec, int minDigits) {
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +0000479 char buffer[SkStrAppendS64_MaxSize];
480 char* stop = SkStrAppendS64(buffer, dec, minDigits);
481 this->insert(offset, buffer, stop - buffer);
482}
483
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000484void SkString::insertHex(size_t offset, uint32_t hex, int minDigits) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000485 minDigits = SkPin32(minDigits, 0, 8);
reed@google.comfa06e522011-02-28 21:29:58 +0000486
reed@android.com8a1c16f2008-12-17 15:59:43 +0000487 static const char gHex[] = "0123456789ABCDEF";
488
489 char buffer[8];
490 char* p = buffer + sizeof(buffer);
491
492 do {
493 *--p = gHex[hex & 0xF];
494 hex >>= 4;
495 minDigits -= 1;
496 } while (hex != 0);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000497
498 while (--minDigits >= 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000499 *--p = '0';
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000500 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000501
502 SkASSERT(p >= buffer);
503 this->insert(offset, p, buffer + sizeof(buffer) - p);
504}
505
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000506void SkString::insertScalar(size_t offset, SkScalar value) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000507 char buffer[SkStrAppendScalar_MaxSize];
508 char* stop = SkStrAppendScalar(buffer, value);
509 this->insert(offset, buffer, stop - buffer);
510}
511
reed@android.com8a1c16f2008-12-17 15:59:43 +0000512void SkString::printf(const char format[], ...) {
513 char buffer[kBufferSize];
514 ARGS_TO_BUFFER(format, buffer, kBufferSize);
515
516 this->set(buffer, strlen(buffer));
517}
518
519void SkString::appendf(const char format[], ...) {
520 char buffer[kBufferSize];
521 ARGS_TO_BUFFER(format, buffer, kBufferSize);
reed@google.comfa06e522011-02-28 21:29:58 +0000522
reed@android.com8a1c16f2008-12-17 15:59:43 +0000523 this->append(buffer, strlen(buffer));
524}
525
526void SkString::prependf(const char format[], ...) {
527 char buffer[kBufferSize];
528 ARGS_TO_BUFFER(format, buffer, kBufferSize);
reed@google.comfa06e522011-02-28 21:29:58 +0000529
reed@android.com8a1c16f2008-12-17 15:59:43 +0000530 this->prepend(buffer, strlen(buffer));
531}
532
reed@google.comfa06e522011-02-28 21:29:58 +0000533///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000534
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000535void SkString::remove(size_t offset, size_t length) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000536 size_t size = this->size();
537
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000538 if (offset < size) {
539 if (offset + length > size) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000540 length = size - offset;
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000541 }
542 if (length > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000543 SkASSERT(size > length);
544 SkString tmp(size - length);
545 char* dst = tmp.writable_str();
546 const char* src = this->c_str();
547
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000548 if (offset) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000549 SkASSERT(offset <= tmp.size());
550 memcpy(dst, src, offset);
551 }
552 size_t tail = size - offset - length;
553 SkASSERT((int32_t)tail >= 0);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000554 if (tail) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000555 // SkASSERT(offset + length <= tmp.size());
556 memcpy(dst + offset, src + offset + length, tail);
557 }
558 SkASSERT(dst[tmp.size()] == 0);
559 this->swap(tmp);
560 }
561 }
562}
563
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000564void SkString::swap(SkString& other) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000565 this->validate();
566 other.validate();
567
568 SkTSwap<Rec*>(fRec, other.fRec);
569#ifdef SK_DEBUG
570 SkTSwap<const char*>(fStr, other.fStr);
571#endif
572}
573
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000574///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000575
mike@reedtribe.orgd5e05d42011-04-10 00:44:32 +0000576SkAutoUCS2::SkAutoUCS2(const char utf8[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000577 size_t len = strlen(utf8);
578 fUCS2 = (uint16_t*)sk_malloc_throw((len + 1) * sizeof(uint16_t));
579
580 uint16_t* dst = fUCS2;
mike@reedtribe.orgd5e05d42011-04-10 00:44:32 +0000581 for (;;) {
582 SkUnichar uni = SkUTF8_NextUnichar(&utf8);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000583 *dst++ = SkToU16(uni);
mike@reedtribe.orgd5e05d42011-04-10 00:44:32 +0000584 if (uni == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000585 break;
mike@reedtribe.orgd5e05d42011-04-10 00:44:32 +0000586 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000587 }
588 fCount = (int)(dst - fUCS2);
589}
590
mike@reedtribe.orgd5e05d42011-04-10 00:44:32 +0000591SkAutoUCS2::~SkAutoUCS2() {
592 sk_free(fUCS2);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000593}
tomhudson@google.com3a1f6a02011-06-30 14:39:52 +0000594
595///////////////////////////////////////////////////////////////////////////////
596
597SkString SkStringPrintf(const char* format, ...) {
598 SkString formattedOutput;
599 char buffer[kBufferSize];
600 ARGS_TO_BUFFER(format, buffer, kBufferSize);
601 formattedOutput.set(buffer);
602 return formattedOutput;
603}
604
605#undef VSNPRINTF
tomhudson@google.com47e0a092011-07-08 17:49:22 +0000606#undef SNPRINTF
tomhudson@google.com3a1f6a02011-06-30 14:39:52 +0000607