blob: 0d7defec8af5e641252bbc7da6defd75c6ecafdc [file] [log] [blame]
reed@android.com8a1c16f2008-12-17 15:59:43 +00001/* libs/graphics/sgl/SkString.cpp
2**
3** Copyright 2006, The Android Open Source Project
4**
reed@google.comfa06e522011-02-28 21:29:58 +00005** Licensed under the Apache License, Version 2.0 (the "License");
6** you may not use this file except in compliance with the License.
7** You may obtain a copy of the License at
reed@android.com8a1c16f2008-12-17 15:59:43 +00008**
reed@google.comfa06e522011-02-28 21:29:58 +00009** http://www.apache.org/licenses/LICENSE-2.0
reed@android.com8a1c16f2008-12-17 15:59:43 +000010**
reed@google.comfa06e522011-02-28 21:29:58 +000011** Unless required by applicable law or agreed to in writing, software
12** distributed under the License is distributed on an "AS IS" BASIS,
13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14** See the License for the specific language governing permissions and
reed@android.com8a1c16f2008-12-17 15:59:43 +000015** limitations under the License.
16*/
17
18#include "SkString.h"
19#include "SkFixed.h"
20#include "SkUtils.h"
21#include <stdarg.h>
reed@google.comfa06e522011-02-28 21:29:58 +000022#include <stdio.h>
23
24// number of bytes (on the stack) to receive the printf result
25static const size_t kBufferSize = 256;
26
27#ifdef SK_BUILD_FOR_WIN
28 #define VSNPRINTF _vsnprintf
29 #define SNPRINTF _snprintf
30#else
31 #define VSNPRINTF vsnprintf
32 #define SNPRINTF snprintf
33#endif
34
35#define ARGS_TO_BUFFER(format, buffer, size) \
36 do { \
37 va_list args; \
38 va_start(args, format); \
39 VSNPRINTF(buffer, size, format, args); \
40 va_end(args); \
41 } while (0)
42
43///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +000044
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +000045bool SkStrStartsWith(const char string[], const char prefix[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000046 SkASSERT(string);
47 SkASSERT(prefix);
48 return !strncmp(string, prefix, strlen(prefix));
49}
50
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +000051bool SkStrEndsWith(const char string[], const char suffix[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000052 SkASSERT(string);
53 SkASSERT(suffix);
54 size_t strLen = strlen(string);
55 size_t suffixLen = strlen(suffix);
56 return strLen >= suffixLen &&
57 !strncmp(string + strLen - suffixLen, suffix, suffixLen);
58}
59
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +000060int SkStrStartsWithOneOf(const char string[], const char prefixes[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000061 int index = 0;
62 do {
63 const char* limit = strchr(prefixes, '\0');
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +000064 if (!strncmp(string, prefixes, limit - prefixes)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000065 return index;
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +000066 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000067 prefixes = limit + 1;
68 index++;
69 } while (prefixes[0]);
70 return -1;
71}
72
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +000073char* SkStrAppendS32(char string[], int32_t dec) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000074 SkDEBUGCODE(char* start = string;)
75
76 char buffer[SkStrAppendS32_MaxSize];
77 char* p = buffer + sizeof(buffer);
78 bool neg = false;
79
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +000080 if (dec < 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000081 neg = true;
82 dec = -dec;
83 }
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +000084
reed@android.com8a1c16f2008-12-17 15:59:43 +000085 do {
86 *--p = SkToU8('0' + dec % 10);
87 dec /= 10;
88 } while (dec != 0);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +000089
90 if (neg) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000091 *--p = '-';
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +000092 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000093
94 SkASSERT(p >= buffer);
95 char* stop = buffer + sizeof(buffer);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +000096 while (p < stop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000097 *string++ = *p++;
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +000098 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000099 SkASSERT(string - start <= SkStrAppendS32_MaxSize);
100 return string;
101}
102
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000103char* SkStrAppendS64(char string[], int64_t dec, int minDigits) {
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +0000104 SkDEBUGCODE(char* start = string;)
105
106 char buffer[SkStrAppendS64_MaxSize];
107 char* p = buffer + sizeof(buffer);
108 bool neg = false;
109
110 if (dec < 0) {
111 neg = true;
112 dec = -dec;
113 }
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000114
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +0000115 do {
116 *--p = SkToU8('0' + dec % 10);
117 dec /= 10;
118 minDigits--;
119 } while (dec != 0);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000120
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +0000121 while (minDigits > 0) {
122 *--p = '0';
123 minDigits--;
124 }
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +0000125
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000126 if (neg) {
127 *--p = '-';
128 }
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +0000129 SkASSERT(p >= buffer);
130 size_t cp_len = buffer + sizeof(buffer) - p;
131 memcpy(string, p, cp_len);
132 string += cp_len;
133
134 SkASSERT(string - start <= SkStrAppendS64_MaxSize);
135 return string;
136}
137
vandebo@chromium.org677cbed2011-03-03 18:20:12 +0000138#ifdef SK_CAN_USE_FLOAT
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000139char* SkStrAppendFloat(char string[], float value) {
reed@google.com8072e4f2011-03-01 15:44:08 +0000140 // since floats have at most 8 significant digits, we limit our %g to that.
141 static const char gFormat[] = "%.8g";
142 // make it 1 larger for the terminating 0
143 char buffer[SkStrAppendScalar_MaxSize + 1];
144 int len = SNPRINTF(buffer, sizeof(buffer), gFormat, value);
145 memcpy(string, buffer, len);
vandebo@chromium.org677cbed2011-03-03 18:20:12 +0000146 SkASSERT(len <= SkStrAppendScalar_MaxSize);
reed@google.com8072e4f2011-03-01 15:44:08 +0000147 return string + len;
vandebo@chromium.org677cbed2011-03-03 18:20:12 +0000148}
149#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000150
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000151char* SkStrAppendFixed(char string[], SkFixed x) {
vandebo@chromium.org677cbed2011-03-03 18:20:12 +0000152 SkDEBUGCODE(char* start = string;)
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000153 if (x < 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000154 *string++ = '-';
155 x = -x;
156 }
157
158 unsigned frac = x & 0xFFFF;
159 x >>= 16;
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000160 if (frac == 0xFFFF) {
161 // 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 +0000162 x += 1;
163 frac = 0;
164 }
165 string = SkStrAppendS32(string, x);
166
167 // now handle the fractional part (if any)
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000168 if (frac) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000169 static const uint16_t gTens[] = { 1000, 100, 10, 1 };
170 const uint16_t* tens = gTens;
171
172 x = SkFixedRound(frac * 10000);
173 SkASSERT(x <= 10000);
174 if (x == 10000) {
175 x -= 1;
176 }
177 *string++ = '.';
178 do {
179 unsigned powerOfTen = *tens++;
180 *string++ = SkToU8('0' + x / powerOfTen);
181 x %= powerOfTen;
182 } while (x != 0);
183 }
reed@google.comfa06e522011-02-28 21:29:58 +0000184
reed@android.com8a1c16f2008-12-17 15:59:43 +0000185 SkASSERT(string - start <= SkStrAppendScalar_MaxSize);
186 return string;
187}
188
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000189///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000190
191#define kMaxRefCnt_SkString SK_MaxU16
192
193// the 3 values are [length] [refcnt] [terminating zero data]
194const SkString::Rec SkString::gEmptyRec = { 0, 0, 0 };
195
196#define SizeOfRec() (gEmptyRec.data() - (const char*)&gEmptyRec)
197
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000198SkString::Rec* SkString::AllocRec(const char text[], U16CPU len) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000199 Rec* rec;
200
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000201 if (len == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000202 rec = const_cast<Rec*>(&gEmptyRec);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000203 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000204 // add 1 for terminating 0, then align4 so we can have some slop when growing the string
205 rec = (Rec*)sk_malloc_throw(SizeOfRec() + SkAlign4(len + 1));
206 rec->fLength = SkToU16(len);
207 rec->fRefCnt = 1;
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000208 if (text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000209 memcpy(rec->data(), text, len);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000210 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000211 rec->data()[len] = 0;
212 }
213 return rec;
214}
215
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000216SkString::Rec* SkString::RefRec(Rec* src) {
217 if (src != &gEmptyRec) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000218 if (src->fRefCnt == kMaxRefCnt_SkString) {
219 src = AllocRec(src->data(), src->fLength);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000220 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000221 src->fRefCnt += 1;
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000222 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000223 }
224 return src;
225}
226
227#ifdef SK_DEBUG
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000228void SkString::validate() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000229 // make sure know one has written over our global
230 SkASSERT(gEmptyRec.fLength == 0);
231 SkASSERT(gEmptyRec.fRefCnt == 0);
232 SkASSERT(gEmptyRec.data()[0] == 0);
233
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000234 if (fRec != &gEmptyRec) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000235 SkASSERT(fRec->fLength > 0);
236 SkASSERT(fRec->fRefCnt > 0);
237 SkASSERT(fRec->data()[fRec->fLength] == 0);
238 }
239 SkASSERT(fStr == c_str());
240}
241#endif
242
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000243///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000244
245SkString::SkString() : fRec(const_cast<Rec*>(&gEmptyRec)) {
246#ifdef SK_DEBUG
247 fStr = fRec->data();
248#endif
249}
250
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000251SkString::SkString(size_t len) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000252 SkASSERT(SkToU16(len) == len); // can't handle larger than 64K
253
254 fRec = AllocRec(NULL, (U16CPU)len);
255#ifdef SK_DEBUG
256 fStr = fRec->data();
257#endif
258}
259
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000260SkString::SkString(const char text[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000261 size_t len = text ? strlen(text) : 0;
262
263 fRec = AllocRec(text, (U16CPU)len);
264#ifdef SK_DEBUG
265 fStr = fRec->data();
266#endif
267}
268
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000269SkString::SkString(const char text[], size_t len) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000270 fRec = AllocRec(text, (U16CPU)len);
271#ifdef SK_DEBUG
272 fStr = fRec->data();
273#endif
274}
275
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000276SkString::SkString(const SkString& src) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000277 src.validate();
278
279 fRec = RefRec(src.fRec);
280#ifdef SK_DEBUG
281 fStr = fRec->data();
282#endif
283}
284
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000285SkString::~SkString() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000286 this->validate();
287
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000288 if (fRec->fLength) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000289 SkASSERT(fRec->fRefCnt > 0);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000290 if (--fRec->fRefCnt == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000291 sk_free(fRec);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000292 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000293 }
294}
295
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000296bool SkString::equals(const SkString& src) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000297 return fRec == src.fRec || this->equals(src.c_str(), src.size());
298}
299
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000300bool SkString::equals(const char text[]) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000301 return this->equals(text, text ? strlen(text) : 0);
302}
303
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000304bool SkString::equals(const char text[], size_t len) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000305 SkASSERT(len == 0 || text != NULL);
306
307 return fRec->fLength == len && !memcmp(fRec->data(), text, len);
308}
309
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000310SkString& SkString::operator=(const SkString& src) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000311 this->validate();
312
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000313 if (fRec != src.fRec) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000314 SkString tmp(src);
315 this->swap(tmp);
316 }
317 return *this;
318}
319
bsalomon@google.comfc296292011-05-06 13:53:47 +0000320SkString& SkString::operator=(const char text[]) {
321 this->validate();
322
323 SkString tmp(text);
324 this->swap(tmp);
325
326 return *this;
327}
328
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000329void SkString::reset() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000330 this->validate();
331
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000332 if (fRec->fLength) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000333 SkASSERT(fRec->fRefCnt > 0);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000334 if (--fRec->fRefCnt == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000335 sk_free(fRec);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000336 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000337 }
338
339 fRec = const_cast<Rec*>(&gEmptyRec);
340#ifdef SK_DEBUG
341 fStr = fRec->data();
342#endif
343}
344
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000345char* SkString::writable_str() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000346 this->validate();
347
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000348 if (fRec->fLength) {
349 if (fRec->fRefCnt > 1) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000350 fRec->fRefCnt -= 1;
351 fRec = AllocRec(fRec->data(), fRec->fLength);
352 #ifdef SK_DEBUG
353 fStr = fRec->data();
354 #endif
355 }
356 }
357 return fRec->data();
358}
359
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000360void SkString::set(const char text[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000361 this->set(text, text ? strlen(text) : 0);
362}
363
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000364void SkString::set(const char text[], size_t len) {
365 if (len == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000366 this->reset();
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000367 } else if (fRec->fRefCnt == 1 && len <= fRec->fLength) {
368 // should we resize if len <<<< fLength, to save RAM? (e.g. len < (fLength>>1))?
reed@android.com8a1c16f2008-12-17 15:59:43 +0000369 // just use less of the buffer without allocating a smaller one
370 char* p = this->writable_str();
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000371 if (text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000372 memcpy(p, text, len);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000373 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000374 p[len] = 0;
375 fRec->fLength = SkToU16(len);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000376 } else if (fRec->fRefCnt == 1 && ((unsigned)fRec->fLength >> 2) == (len >> 2)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000377 // we have spare room in the current allocation, so don't alloc a larger one
378 char* p = this->writable_str();
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000379 if (text) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000380 memcpy(p, text, len);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000381 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000382 p[len] = 0;
383 fRec->fLength = SkToU16(len);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000384 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000385 SkString tmp(text, len);
386 this->swap(tmp);
387 }
388}
389
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000390void SkString::setUTF16(const uint16_t src[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000391 int count = 0;
392
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000393 while (src[count]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000394 count += 1;
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000395 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000396 setUTF16(src, count);
397}
398
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000399void SkString::setUTF16(const uint16_t src[], size_t count) {
400 if (count == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000401 this->reset();
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000402 } else if (count <= fRec->fLength) {
403 // should we resize if len <<<< fLength, to save RAM? (e.g. len < (fLength>>1))
404 if (count < fRec->fLength) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000405 this->resize(count);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000406 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000407 char* p = this->writable_str();
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000408 for (size_t i = 0; i < count; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000409 p[i] = SkToU8(src[i]);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000410 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000411 p[count] = 0;
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000412 } else {
413 SkString tmp(count); // puts a null terminator at the end of the string
414 char* p = tmp.writable_str();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000415
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000416 for (size_t i = 0; i < count; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000417 p[i] = SkToU8(src[i]);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000418 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000419 this->swap(tmp);
420 }
421}
422
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000423void SkString::insert(size_t offset, const char text[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000424 this->insert(offset, text, text ? strlen(text) : 0);
425}
426
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000427void SkString::insert(size_t offset, const char text[], size_t len) {
428 if (len) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000429 size_t length = fRec->fLength;
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000430 if (offset > length) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000431 offset = length;
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000432 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000433
434 /* If we're the only owner, and we have room in our allocation for the insert,
435 do it in place, rather than allocating a new buffer.
436
437 To know we have room, compare the allocated sizes
438 beforeAlloc = SkAlign4(length + 1)
439 afterAlloc = SkAligh4(length + 1 + len)
440 but SkAlign4(x) is (x + 3) >> 2 << 2
441 which is equivalent for testing to (length + 1 + 3) >> 2 == (length + 1 + 3 + len) >> 2
442 and we can then eliminate the +1+3 since that doesn't affec the answer
443 */
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000444 if (fRec->fRefCnt == 1 && (length >> 2) == ((length + len) >> 2)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000445 char* dst = this->writable_str();
446
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000447 if (offset < length) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000448 memmove(dst + offset + len, dst + offset, length - offset);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000449 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000450 memcpy(dst + offset, text, len);
451
452 dst[length + len] = 0;
453 fRec->fLength = SkToU16(length + len);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000454 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000455 /* Seems we should use realloc here, since that is safe if it fails
456 (we have the original data), and might be faster than alloc/copy/free.
457 */
458 SkString tmp(fRec->fLength + len);
459 char* dst = tmp.writable_str();
460
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000461 if (offset > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000462 memcpy(dst, fRec->data(), offset);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000463 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000464 memcpy(dst + offset, text, len);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000465 if (offset < fRec->fLength) {
466 memcpy(dst + offset + len, fRec->data() + offset,
467 fRec->fLength - offset);
468 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000469
470 this->swap(tmp);
471 }
472 }
473}
474
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000475void SkString::insertUnichar(size_t offset, SkUnichar uni) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000476 char buffer[kMaxBytesInUTF8Sequence];
477 size_t len = SkUTF8_FromUnichar(uni, buffer);
478
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000479 if (len) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000480 this->insert(offset, buffer, len);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000481 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000482}
483
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000484void SkString::insertS32(size_t offset, int32_t dec) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000485 char buffer[SkStrAppendS32_MaxSize];
486 char* stop = SkStrAppendS32(buffer, dec);
487 this->insert(offset, buffer, stop - buffer);
488}
489
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000490void SkString::insertS64(size_t offset, int64_t dec, int minDigits) {
vandebo@chromium.orgd877fdb2010-10-12 23:08:13 +0000491 char buffer[SkStrAppendS64_MaxSize];
492 char* stop = SkStrAppendS64(buffer, dec, minDigits);
493 this->insert(offset, buffer, stop - buffer);
494}
495
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000496void SkString::insertHex(size_t offset, uint32_t hex, int minDigits) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000497 minDigits = SkPin32(minDigits, 0, 8);
reed@google.comfa06e522011-02-28 21:29:58 +0000498
reed@android.com8a1c16f2008-12-17 15:59:43 +0000499 static const char gHex[] = "0123456789ABCDEF";
500
501 char buffer[8];
502 char* p = buffer + sizeof(buffer);
503
504 do {
505 *--p = gHex[hex & 0xF];
506 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[], ...) {
525 char buffer[kBufferSize];
526 ARGS_TO_BUFFER(format, buffer, kBufferSize);
527
528 this->set(buffer, strlen(buffer));
529}
530
531void SkString::appendf(const char format[], ...) {
532 char buffer[kBufferSize];
533 ARGS_TO_BUFFER(format, buffer, kBufferSize);
reed@google.comfa06e522011-02-28 21:29:58 +0000534
reed@android.com8a1c16f2008-12-17 15:59:43 +0000535 this->append(buffer, strlen(buffer));
536}
537
538void SkString::prependf(const char format[], ...) {
539 char buffer[kBufferSize];
540 ARGS_TO_BUFFER(format, buffer, kBufferSize);
reed@google.comfa06e522011-02-28 21:29:58 +0000541
reed@android.com8a1c16f2008-12-17 15:59:43 +0000542 this->prepend(buffer, strlen(buffer));
543}
544
reed@google.comfa06e522011-02-28 21:29:58 +0000545///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000546
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000547void SkString::remove(size_t offset, size_t length) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000548 size_t size = this->size();
549
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000550 if (offset < size) {
551 if (offset + length > size) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000552 length = size - offset;
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000553 }
554 if (length > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000555 SkASSERT(size > length);
556 SkString tmp(size - length);
557 char* dst = tmp.writable_str();
558 const char* src = this->c_str();
559
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000560 if (offset) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000561 SkASSERT(offset <= tmp.size());
562 memcpy(dst, src, offset);
563 }
564 size_t tail = size - offset - length;
565 SkASSERT((int32_t)tail >= 0);
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000566 if (tail) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000567 // SkASSERT(offset + length <= tmp.size());
568 memcpy(dst + offset, src + offset + length, tail);
569 }
570 SkASSERT(dst[tmp.size()] == 0);
571 this->swap(tmp);
572 }
573 }
574}
575
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000576void SkString::swap(SkString& other) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000577 this->validate();
578 other.validate();
579
580 SkTSwap<Rec*>(fRec, other.fRec);
581#ifdef SK_DEBUG
582 SkTSwap<const char*>(fStr, other.fStr);
583#endif
584}
585
mike@reedtribe.org4e1d3ac2011-04-10 01:04:37 +0000586///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000587
mike@reedtribe.orgd5e05d42011-04-10 00:44:32 +0000588SkAutoUCS2::SkAutoUCS2(const char utf8[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000589 size_t len = strlen(utf8);
590 fUCS2 = (uint16_t*)sk_malloc_throw((len + 1) * sizeof(uint16_t));
591
592 uint16_t* dst = fUCS2;
mike@reedtribe.orgd5e05d42011-04-10 00:44:32 +0000593 for (;;) {
594 SkUnichar uni = SkUTF8_NextUnichar(&utf8);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000595 *dst++ = SkToU16(uni);
mike@reedtribe.orgd5e05d42011-04-10 00:44:32 +0000596 if (uni == 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000597 break;
mike@reedtribe.orgd5e05d42011-04-10 00:44:32 +0000598 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000599 }
600 fCount = (int)(dst - fUCS2);
601}
602
mike@reedtribe.orgd5e05d42011-04-10 00:44:32 +0000603SkAutoUCS2::~SkAutoUCS2() {
604 sk_free(fUCS2);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000605}
tomhudson@google.com3a1f6a02011-06-30 14:39:52 +0000606
607///////////////////////////////////////////////////////////////////////////////
608
609SkString SkStringPrintf(const char* format, ...) {
610 SkString formattedOutput;
611 char buffer[kBufferSize];
612 ARGS_TO_BUFFER(format, buffer, kBufferSize);
613 formattedOutput.set(buffer);
614 return formattedOutput;
615}
616
617#undef VSNPRINTF
618