blob: 4658ff8cb1aadc4c2a42d2f7f4e8694f2caff8ec [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 SkStrStartsWith(const char string[], const char prefix[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000040 SkASSERT(string);
41 SkASSERT(prefix);
42 return !strncmp(string, prefix, strlen(prefix));
43}
44
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +000045bool SkStrEndsWith(const char string[], const char suffix[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000046 SkASSERT(string);
47 SkASSERT(suffix);
48 size_t strLen = strlen(string);
49 size_t suffixLen = strlen(suffix);
50 return strLen >= suffixLen &&
51 !strncmp(string + strLen - suffixLen, suffix, suffixLen);
52}
53
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +000054int SkStrStartsWithOneOf(const char string[], const char prefixes[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000055 int index = 0;
56 do {
57 const char* limit = strchr(prefixes, '\0');
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +000058 if (!strncmp(string, prefixes, limit - prefixes)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000059 return index;
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +000060 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000061 prefixes = limit + 1;
62 index++;
63 } while (prefixes[0]);
64 return -1;
65}
66
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +000067char* SkStrAppendS32(char string[], int32_t dec) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000068 SkDEBUGCODE(char* start = string;)
69
70 char buffer[SkStrAppendS32_MaxSize];
71 char* p = buffer + sizeof(buffer);
72 bool neg = false;
73
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +000074 if (dec < 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000075 neg = true;
76 dec = -dec;
77 }
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +000078
reed@android.com8a1c16f2008-12-17 15:59:43 +000079 do {
80 *--p = SkToU8('0' + dec % 10);
81 dec /= 10;
82 } while (dec != 0);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +000083
84 if (neg) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000085 *--p = '-';
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +000086 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000087
88 SkASSERT(p >= buffer);
89 char* stop = buffer + sizeof(buffer);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +000090 while (p < stop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000091 *string++ = *p++;
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +000092 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000093 SkASSERT(string - start <= SkStrAppendS32_MaxSize);
94 return string;
95}
96
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +000097char* SkStrAppendS64(char string[], int64_t dec, int minDigits) {
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +000098 SkDEBUGCODE(char* start = string;)
99
100 char buffer[SkStrAppendS64_MaxSize];
101 char* p = buffer + sizeof(buffer);
102 bool neg = false;
103
104 if (dec < 0) {
105 neg = true;
106 dec = -dec;
107 }
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000108
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +0000109 do {
110 *--p = SkToU8('0' + dec % 10);
111 dec /= 10;
112 minDigits--;
113 } while (dec != 0);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000114
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +0000115 while (minDigits > 0) {
116 *--p = '0';
117 minDigits--;
118 }
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +0000119
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000120 if (neg) {
121 *--p = '-';
122 }
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +0000123 SkASSERT(p >= buffer);
124 size_t cp_len = buffer + sizeof(buffer) - p;
125 memcpy(string, p, cp_len);
126 string += cp_len;
127
128 SkASSERT(string - start <= SkStrAppendS64_MaxSize);
129 return string;
130}
131
vandebo@chromium.org677cbed2011-03-03 18:20:12 +0000132#ifdef SK_CAN_USE_FLOAT
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000133char* SkStrAppendFloat(char string[], float value) {
reed@google.com8072e4f2011-03-01 15:44:08 +0000134 // since floats have at most 8 significant digits, we limit our %g to that.
135 static const char gFormat[] = "%.8g";
136 // make it 1 larger for the terminating 0
137 char buffer[SkStrAppendScalar_MaxSize + 1];
138 int len = SNPRINTF(buffer, sizeof(buffer), gFormat, value);
139 memcpy(string, buffer, len);
vandebo@chromium.org677cbed2011-03-03 18:20:12 +0000140 SkASSERT(len <= SkStrAppendScalar_MaxSize);
reed@google.com8072e4f2011-03-01 15:44:08 +0000141 return string + len;
vandebo@chromium.org677cbed2011-03-03 18:20:12 +0000142}
143#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000144
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000145char* SkStrAppendFixed(char string[], SkFixed x) {
vandebo@chromium.org677cbed2011-03-03 18:20:12 +0000146 SkDEBUGCODE(char* start = string;)
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000147 if (x < 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000148 *string++ = '-';
149 x = -x;
150 }
151
152 unsigned frac = x & 0xFFFF;
153 x >>= 16;
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000154 if (frac == 0xFFFF) {
155 // 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 +0000156 x += 1;
157 frac = 0;
158 }
159 string = SkStrAppendS32(string, x);
160
161 // now handle the fractional part (if any)
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000162 if (frac) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000163 static const uint16_t gTens[] = { 1000, 100, 10, 1 };
164 const uint16_t* tens = gTens;
165
166 x = SkFixedRound(frac * 10000);
167 SkASSERT(x <= 10000);
168 if (x == 10000) {
169 x -= 1;
170 }
171 *string++ = '.';
172 do {
173 unsigned powerOfTen = *tens++;
174 *string++ = SkToU8('0' + x / powerOfTen);
175 x %= powerOfTen;
176 } while (x != 0);
177 }
reed@google.comfa06e522011-02-28 21:29:58 +0000178
reed@android.com8a1c16f2008-12-17 15:59:43 +0000179 SkASSERT(string - start <= SkStrAppendScalar_MaxSize);
180 return string;
181}
182
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000183///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000184
reed@android.com8a1c16f2008-12-17 15:59:43 +0000185// the 3 values are [length] [refcnt] [terminating zero data]
186const SkString::Rec SkString::gEmptyRec = { 0, 0, 0 };
187
188#define SizeOfRec() (gEmptyRec.data() - (const char*)&gEmptyRec)
189
reed@google.com4bce1152011-09-14 16:13:58 +0000190SkString::Rec* SkString::AllocRec(const char text[], size_t len) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000191 Rec* rec;
192
reed@google.com4bce1152011-09-14 16:13:58 +0000193 if (0 == len) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000194 rec = const_cast<Rec*>(&gEmptyRec);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000195 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000196 // add 1 for terminating 0, then align4 so we can have some slop when growing the string
197 rec = (Rec*)sk_malloc_throw(SizeOfRec() + SkAlign4(len + 1));
reed@google.com4bce1152011-09-14 16:13:58 +0000198 rec->fLength = len;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000199 rec->fRefCnt = 1;
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000200 if (text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000201 memcpy(rec->data(), text, len);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000202 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000203 rec->data()[len] = 0;
204 }
205 return rec;
206}
207
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000208SkString::Rec* SkString::RefRec(Rec* src) {
209 if (src != &gEmptyRec) {
reed@google.com4bce1152011-09-14 16:13:58 +0000210 sk_atomic_inc(&src->fRefCnt);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000211 }
212 return src;
213}
214
215#ifdef SK_DEBUG
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000216void SkString::validate() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000217 // make sure know one has written over our global
reed@google.com4bce1152011-09-14 16:13:58 +0000218 SkASSERT(0 == gEmptyRec.fLength);
219 SkASSERT(0 == gEmptyRec.fRefCnt);
220 SkASSERT(0 == gEmptyRec.data()[0]);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000221
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000222 if (fRec != &gEmptyRec) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000223 SkASSERT(fRec->fLength > 0);
224 SkASSERT(fRec->fRefCnt > 0);
reed@google.com4bce1152011-09-14 16:13:58 +0000225 SkASSERT(0 == fRec->data()[fRec->fLength]);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000226 }
227 SkASSERT(fStr == c_str());
228}
229#endif
230
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000231///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000232
233SkString::SkString() : fRec(const_cast<Rec*>(&gEmptyRec)) {
234#ifdef SK_DEBUG
235 fStr = fRec->data();
236#endif
237}
238
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000239SkString::SkString(size_t len) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000240 SkASSERT(SkToU16(len) == len); // can't handle larger than 64K
241
242 fRec = AllocRec(NULL, (U16CPU)len);
243#ifdef SK_DEBUG
244 fStr = fRec->data();
245#endif
246}
247
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000248SkString::SkString(const char text[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000249 size_t len = text ? strlen(text) : 0;
250
251 fRec = AllocRec(text, (U16CPU)len);
252#ifdef SK_DEBUG
253 fStr = fRec->data();
254#endif
255}
256
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000257SkString::SkString(const char text[], size_t len) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000258 fRec = AllocRec(text, (U16CPU)len);
259#ifdef SK_DEBUG
260 fStr = fRec->data();
261#endif
262}
263
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000264SkString::SkString(const SkString& src) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000265 src.validate();
266
267 fRec = RefRec(src.fRec);
268#ifdef SK_DEBUG
269 fStr = fRec->data();
270#endif
271}
272
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000273SkString::~SkString() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000274 this->validate();
275
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000276 if (fRec->fLength) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000277 SkASSERT(fRec->fRefCnt > 0);
reed@google.com4bce1152011-09-14 16:13:58 +0000278 if (sk_atomic_dec(&fRec->fRefCnt) == 1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000279 sk_free(fRec);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000280 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000281 }
282}
283
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000284bool SkString::equals(const SkString& src) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000285 return fRec == src.fRec || this->equals(src.c_str(), src.size());
286}
287
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000288bool SkString::equals(const char text[]) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000289 return this->equals(text, text ? strlen(text) : 0);
290}
291
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000292bool SkString::equals(const char text[], size_t len) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000293 SkASSERT(len == 0 || text != NULL);
294
295 return fRec->fLength == len && !memcmp(fRec->data(), text, len);
296}
297
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000298SkString& SkString::operator=(const SkString& src) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000299 this->validate();
300
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000301 if (fRec != src.fRec) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000302 SkString tmp(src);
303 this->swap(tmp);
304 }
305 return *this;
306}
307
bsalomon@google.comfc296292011-05-06 13:53:47 +0000308SkString& SkString::operator=(const char text[]) {
309 this->validate();
310
311 SkString tmp(text);
312 this->swap(tmp);
313
314 return *this;
315}
316
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000317void SkString::reset() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000318 this->validate();
319
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000320 if (fRec->fLength) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000321 SkASSERT(fRec->fRefCnt > 0);
reed@google.com4bce1152011-09-14 16:13:58 +0000322 if (sk_atomic_dec(&fRec->fRefCnt) == 1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000323 sk_free(fRec);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000324 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000325 }
326
327 fRec = const_cast<Rec*>(&gEmptyRec);
328#ifdef SK_DEBUG
329 fStr = fRec->data();
330#endif
331}
332
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000333char* SkString::writable_str() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000334 this->validate();
335
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000336 if (fRec->fLength) {
337 if (fRec->fRefCnt > 1) {
reed@google.com4bce1152011-09-14 16:13:58 +0000338 Rec* rec = AllocRec(fRec->data(), fRec->fLength);
339 if (sk_atomic_dec(&fRec->fRefCnt) == 1) {
340 // In this case after our check of fRecCnt > 1, we suddenly
341 // did become the only owner, so now we have two copies of the
342 // data (fRec and rec), so we need to delete one of them.
343 sk_free(fRec);
344 }
345 fRec = rec;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000346 #ifdef SK_DEBUG
347 fStr = fRec->data();
348 #endif
349 }
350 }
351 return fRec->data();
352}
353
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000354void SkString::set(const char text[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000355 this->set(text, text ? strlen(text) : 0);
356}
357
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000358void SkString::set(const char text[], size_t len) {
reed@google.com4bce1152011-09-14 16:13:58 +0000359 if (0 == len) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000360 this->reset();
reed@google.com4bce1152011-09-14 16:13:58 +0000361 } else if (1 == fRec->fRefCnt && 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.com4bce1152011-09-14 16:13:58 +0000369 fRec->fLength = len;
370 } else if (1 == fRec->fRefCnt && (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.com4bce1152011-09-14 16:13:58 +0000377 fRec->fLength = 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::setUTF16(const uint16_t src[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000385 int count = 0;
386
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000387 while (src[count]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000388 count += 1;
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000389 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000390 setUTF16(src, count);
391}
392
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000393void SkString::setUTF16(const uint16_t src[], size_t count) {
reed@google.com4bce1152011-09-14 16:13:58 +0000394 if (0 == count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000395 this->reset();
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000396 } else if (count <= fRec->fLength) {
397 // should we resize if len <<<< fLength, to save RAM? (e.g. len < (fLength>>1))
398 if (count < fRec->fLength) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000399 this->resize(count);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000400 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000401 char* p = this->writable_str();
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000402 for (size_t i = 0; i < count; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000403 p[i] = SkToU8(src[i]);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000404 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000405 p[count] = 0;
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000406 } else {
407 SkString tmp(count); // puts a null terminator at the end of the string
408 char* p = tmp.writable_str();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000409
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000410 for (size_t i = 0; i < count; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000411 p[i] = SkToU8(src[i]);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000412 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000413 this->swap(tmp);
414 }
415}
416
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000417void SkString::insert(size_t offset, const char text[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000418 this->insert(offset, text, text ? strlen(text) : 0);
419}
420
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000421void SkString::insert(size_t offset, const char text[], size_t len) {
422 if (len) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000423 size_t length = fRec->fLength;
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000424 if (offset > length) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000425 offset = length;
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000426 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000427
428 /* If we're the only owner, and we have room in our allocation for the insert,
429 do it in place, rather than allocating a new buffer.
430
431 To know we have room, compare the allocated sizes
432 beforeAlloc = SkAlign4(length + 1)
433 afterAlloc = SkAligh4(length + 1 + len)
434 but SkAlign4(x) is (x + 3) >> 2 << 2
435 which is equivalent for testing to (length + 1 + 3) >> 2 == (length + 1 + 3 + len) >> 2
436 and we can then eliminate the +1+3 since that doesn't affec the answer
437 */
reed@google.com4bce1152011-09-14 16:13:58 +0000438 if (1 == fRec->fRefCnt && (length >> 2) == ((length + len) >> 2)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000439 char* dst = this->writable_str();
440
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000441 if (offset < length) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000442 memmove(dst + offset + len, dst + offset, length - offset);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000443 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000444 memcpy(dst + offset, text, len);
445
446 dst[length + len] = 0;
reed@google.com4bce1152011-09-14 16:13:58 +0000447 fRec->fLength = length + len;
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000448 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000449 /* Seems we should use realloc here, since that is safe if it fails
450 (we have the original data), and might be faster than alloc/copy/free.
451 */
452 SkString tmp(fRec->fLength + len);
453 char* dst = tmp.writable_str();
454
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000455 if (offset > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000456 memcpy(dst, fRec->data(), offset);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000457 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000458 memcpy(dst + offset, text, len);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000459 if (offset < fRec->fLength) {
460 memcpy(dst + offset + len, fRec->data() + offset,
461 fRec->fLength - offset);
462 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000463
464 this->swap(tmp);
465 }
466 }
467}
468
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000469void SkString::insertUnichar(size_t offset, SkUnichar uni) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000470 char buffer[kMaxBytesInUTF8Sequence];
471 size_t len = SkUTF8_FromUnichar(uni, buffer);
472
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000473 if (len) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000474 this->insert(offset, buffer, len);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000475 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000476}
477
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000478void SkString::insertS32(size_t offset, int32_t dec) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000479 char buffer[SkStrAppendS32_MaxSize];
480 char* stop = SkStrAppendS32(buffer, dec);
481 this->insert(offset, buffer, stop - buffer);
482}
483
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000484void SkString::insertS64(size_t offset, int64_t dec, int minDigits) {
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +0000485 char buffer[SkStrAppendS64_MaxSize];
486 char* stop = SkStrAppendS64(buffer, dec, minDigits);
487 this->insert(offset, buffer, stop - buffer);
488}
489
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000490void SkString::insertHex(size_t offset, uint32_t hex, int minDigits) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000491 minDigits = SkPin32(minDigits, 0, 8);
reed@google.comfa06e522011-02-28 21:29:58 +0000492
reed@android.com8a1c16f2008-12-17 15:59:43 +0000493 static const char gHex[] = "0123456789ABCDEF";
494
495 char buffer[8];
496 char* p = buffer + sizeof(buffer);
497
498 do {
499 *--p = gHex[hex & 0xF];
500 hex >>= 4;
501 minDigits -= 1;
502 } while (hex != 0);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000503
504 while (--minDigits >= 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000505 *--p = '0';
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000506 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000507
508 SkASSERT(p >= buffer);
509 this->insert(offset, p, buffer + sizeof(buffer) - p);
510}
511
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000512void SkString::insertScalar(size_t offset, SkScalar value) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000513 char buffer[SkStrAppendScalar_MaxSize];
514 char* stop = SkStrAppendScalar(buffer, value);
515 this->insert(offset, buffer, stop - buffer);
516}
517
reed@android.com8a1c16f2008-12-17 15:59:43 +0000518void SkString::printf(const char format[], ...) {
519 char buffer[kBufferSize];
520 ARGS_TO_BUFFER(format, buffer, kBufferSize);
521
522 this->set(buffer, strlen(buffer));
523}
524
525void SkString::appendf(const char format[], ...) {
526 char buffer[kBufferSize];
527 ARGS_TO_BUFFER(format, buffer, kBufferSize);
reed@google.comfa06e522011-02-28 21:29:58 +0000528
reed@android.com8a1c16f2008-12-17 15:59:43 +0000529 this->append(buffer, strlen(buffer));
530}
531
532void SkString::prependf(const char format[], ...) {
533 char buffer[kBufferSize];
534 ARGS_TO_BUFFER(format, buffer, kBufferSize);
reed@google.comfa06e522011-02-28 21:29:58 +0000535
reed@android.com8a1c16f2008-12-17 15:59:43 +0000536 this->prepend(buffer, strlen(buffer));
537}
538
reed@google.comfa06e522011-02-28 21:29:58 +0000539///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000540
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000541void SkString::remove(size_t offset, size_t length) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000542 size_t size = this->size();
543
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000544 if (offset < size) {
545 if (offset + length > size) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000546 length = size - offset;
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000547 }
548 if (length > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000549 SkASSERT(size > length);
550 SkString tmp(size - length);
551 char* dst = tmp.writable_str();
552 const char* src = this->c_str();
553
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000554 if (offset) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000555 SkASSERT(offset <= tmp.size());
556 memcpy(dst, src, offset);
557 }
558 size_t tail = size - offset - length;
559 SkASSERT((int32_t)tail >= 0);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000560 if (tail) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000561 // SkASSERT(offset + length <= tmp.size());
562 memcpy(dst + offset, src + offset + length, tail);
563 }
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
574 SkTSwap<Rec*>(fRec, other.fRec);
575#ifdef SK_DEBUG
576 SkTSwap<const char*>(fStr, other.fStr);
577#endif
578}
579
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000580///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000581
mike@reedtribe.orgd5e05d42011-04-10 00:44:32 +0000582SkAutoUCS2::SkAutoUCS2(const char utf8[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000583 size_t len = strlen(utf8);
584 fUCS2 = (uint16_t*)sk_malloc_throw((len + 1) * sizeof(uint16_t));
585
586 uint16_t* dst = fUCS2;
mike@reedtribe.orgd5e05d42011-04-10 00:44:32 +0000587 for (;;) {
588 SkUnichar uni = SkUTF8_NextUnichar(&utf8);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000589 *dst++ = SkToU16(uni);
mike@reedtribe.orgd5e05d42011-04-10 00:44:32 +0000590 if (uni == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000591 break;
mike@reedtribe.orgd5e05d42011-04-10 00:44:32 +0000592 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000593 }
594 fCount = (int)(dst - fUCS2);
595}
596
mike@reedtribe.orgd5e05d42011-04-10 00:44:32 +0000597SkAutoUCS2::~SkAutoUCS2() {
598 sk_free(fUCS2);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000599}
tomhudson@google.com3a1f6a02011-06-30 14:39:52 +0000600
601///////////////////////////////////////////////////////////////////////////////
602
603SkString SkStringPrintf(const char* format, ...) {
604 SkString formattedOutput;
605 char buffer[kBufferSize];
606 ARGS_TO_BUFFER(format, buffer, kBufferSize);
607 formattedOutput.set(buffer);
608 return formattedOutput;
609}
610
611#undef VSNPRINTF
tomhudson@google.com47e0a092011-07-08 17:49:22 +0000612#undef SNPRINTF
tomhudson@google.com3a1f6a02011-06-30 14:39:52 +0000613