Line data Source code
1 : /*
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 :
8 :
9 : #include "SkAtomics.h"
10 : #include "SkString.h"
11 : #include "SkUtils.h"
12 : #include <stdarg.h>
13 : #include <stdio.h>
14 :
15 : // number of bytes (on the stack) to receive the printf result
16 : static const size_t kBufferSize = 1024;
17 :
18 : #if defined(_MSC_VER) && _MSC_VER < 1900
19 : #define VSNPRINTF(buffer, size, format, args) \
20 : _vsnprintf_s(buffer, size, _TRUNCATE, format, args)
21 : #define SNPRINTF _snprintf
22 : #else
23 : #define VSNPRINTF vsnprintf
24 : #define SNPRINTF snprintf
25 : #endif
26 :
27 : #define ARGS_TO_BUFFER(format, buffer, size, written) \
28 : do { \
29 : va_list args; \
30 : va_start(args, format); \
31 : written = VSNPRINTF(buffer, size, format, args); \
32 : SkASSERT(written >= 0 && written < SkToInt(size)); \
33 : va_end(args); \
34 : } while (0)
35 :
36 : #if defined(_MSC_VER) && _MSC_VER < 1900
37 : #define V_SKSTRING_PRINTF(output, format) \
38 : do { \
39 : va_list args; \
40 : va_start(args, format); \
41 : char buffer[kBufferSize]; \
42 : int length = _vsnprintf_s(buffer, sizeof(buffer), \
43 : _TRUNCATE, format, args); \
44 : va_end(args); \
45 : if (length >= 0 && length < (int)sizeof(buffer)) { \
46 : output.set(buffer, length); \
47 : break; \
48 : } \
49 : va_start(args, format); \
50 : length = _vscprintf(format, args); \
51 : va_end(args); \
52 : SkAutoTMalloc<char> autoTMalloc((size_t)length + 1); \
53 : va_start(args, format); \
54 : SkDEBUGCODE(int check = ) _vsnprintf_s(autoTMalloc.get(), \
55 : length + 1, _TRUNCATE, \
56 : format, args); \
57 : va_end(args); \
58 : SkASSERT(check == length); \
59 : output.set(autoTMalloc.get(), length); \
60 : SkASSERT(output[length] == '\0'); \
61 : } while (false)
62 : #else
63 : #define V_SKSTRING_PRINTF(output, format) \
64 : do { \
65 : va_list args; \
66 : va_start(args, format); \
67 : char buffer[kBufferSize]; \
68 : int length = vsnprintf(buffer, sizeof(buffer), format, args); \
69 : va_end(args); \
70 : if (length < 0) { \
71 : break; \
72 : } \
73 : if (length < (int)sizeof(buffer)) { \
74 : output.set(buffer, length); \
75 : break; \
76 : } \
77 : SkAutoTMalloc<char> autoTMalloc((size_t)length + 1); \
78 : va_start(args, format); \
79 : SkDEBUGCODE(int check = ) vsnprintf(autoTMalloc.get(), \
80 : length + 1, format, args); \
81 : va_end(args); \
82 : SkASSERT(check == length); \
83 : output.set(autoTMalloc.get(), length); \
84 : SkASSERT(output[length] == '\0'); \
85 : } while (false)
86 : #endif
87 :
88 : ///////////////////////////////////////////////////////////////////////////////
89 :
90 0 : bool SkStrEndsWith(const char string[], const char suffixStr[]) {
91 0 : SkASSERT(string);
92 0 : SkASSERT(suffixStr);
93 0 : size_t strLen = strlen(string);
94 0 : size_t suffixLen = strlen(suffixStr);
95 0 : return strLen >= suffixLen &&
96 0 : !strncmp(string + strLen - suffixLen, suffixStr, suffixLen);
97 : }
98 :
99 0 : bool SkStrEndsWith(const char string[], const char suffixChar) {
100 0 : SkASSERT(string);
101 0 : size_t strLen = strlen(string);
102 0 : if (0 == strLen) {
103 0 : return false;
104 : } else {
105 0 : return (suffixChar == string[strLen-1]);
106 : }
107 : }
108 :
109 0 : int SkStrStartsWithOneOf(const char string[], const char prefixes[]) {
110 0 : int index = 0;
111 0 : do {
112 0 : const char* limit = strchr(prefixes, '\0');
113 0 : if (!strncmp(string, prefixes, limit - prefixes)) {
114 0 : return index;
115 : }
116 0 : prefixes = limit + 1;
117 0 : index++;
118 0 : } while (prefixes[0]);
119 0 : return -1;
120 : }
121 :
122 0 : char* SkStrAppendU32(char string[], uint32_t dec) {
123 0 : SkDEBUGCODE(char* start = string;)
124 :
125 : char buffer[SkStrAppendU32_MaxSize];
126 0 : char* p = buffer + sizeof(buffer);
127 :
128 0 : do {
129 0 : *--p = SkToU8('0' + dec % 10);
130 0 : dec /= 10;
131 0 : } while (dec != 0);
132 :
133 0 : SkASSERT(p >= buffer);
134 0 : char* stop = buffer + sizeof(buffer);
135 0 : while (p < stop) {
136 0 : *string++ = *p++;
137 : }
138 0 : SkASSERT(string - start <= SkStrAppendU32_MaxSize);
139 0 : return string;
140 : }
141 :
142 0 : char* SkStrAppendS32(char string[], int32_t dec) {
143 0 : uint32_t udec = dec;
144 0 : if (dec < 0) {
145 0 : *string++ = '-';
146 0 : udec = ~udec + 1; // udec = -udec, but silences some warnings that are trying to be helpful
147 : }
148 0 : return SkStrAppendU32(string, udec);
149 : }
150 :
151 0 : char* SkStrAppendU64(char string[], uint64_t dec, int minDigits) {
152 0 : SkDEBUGCODE(char* start = string;)
153 :
154 : char buffer[SkStrAppendU64_MaxSize];
155 0 : char* p = buffer + sizeof(buffer);
156 :
157 0 : do {
158 0 : *--p = SkToU8('0' + (int32_t) (dec % 10));
159 0 : dec /= 10;
160 0 : minDigits--;
161 0 : } while (dec != 0);
162 :
163 0 : while (minDigits > 0) {
164 0 : *--p = '0';
165 0 : minDigits--;
166 : }
167 :
168 0 : SkASSERT(p >= buffer);
169 0 : size_t cp_len = buffer + sizeof(buffer) - p;
170 0 : memcpy(string, p, cp_len);
171 0 : string += cp_len;
172 :
173 0 : SkASSERT(string - start <= SkStrAppendU64_MaxSize);
174 0 : return string;
175 : }
176 :
177 0 : char* SkStrAppendS64(char string[], int64_t dec, int minDigits) {
178 0 : uint64_t udec = dec;
179 0 : if (dec < 0) {
180 0 : *string++ = '-';
181 0 : udec = ~udec + 1; // udec = -udec, but silences some warnings that are trying to be helpful
182 : }
183 0 : return SkStrAppendU64(string, udec, minDigits);
184 : }
185 :
186 0 : char* SkStrAppendFloat(char string[], float value) {
187 : // since floats have at most 8 significant digits, we limit our %g to that.
188 : static const char gFormat[] = "%.8g";
189 : // make it 1 larger for the terminating 0
190 : char buffer[SkStrAppendScalar_MaxSize + 1];
191 0 : int len = SNPRINTF(buffer, sizeof(buffer), gFormat, value);
192 0 : memcpy(string, buffer, len);
193 0 : SkASSERT(len <= SkStrAppendScalar_MaxSize);
194 0 : return string + len;
195 : }
196 :
197 : ///////////////////////////////////////////////////////////////////////////////
198 :
199 : // the 3 values are [length] [refcnt] [terminating zero data]
200 : const SkString::Rec SkString::gEmptyRec = { 0, 0, 0 };
201 :
202 : #define SizeOfRec() (gEmptyRec.data() - (const char*)&gEmptyRec)
203 :
204 0 : static uint32_t trim_size_t_to_u32(size_t value) {
205 : if (sizeof(size_t) > sizeof(uint32_t)) {
206 0 : if (value > SK_MaxU32) {
207 0 : value = SK_MaxU32;
208 : }
209 : }
210 0 : return (uint32_t)value;
211 : }
212 :
213 0 : static size_t check_add32(size_t base, size_t extra) {
214 0 : SkASSERT(base <= SK_MaxU32);
215 : if (sizeof(size_t) > sizeof(uint32_t)) {
216 0 : if (base + extra > SK_MaxU32) {
217 0 : extra = SK_MaxU32 - base;
218 : }
219 : }
220 0 : return extra;
221 : }
222 :
223 0 : SkString::Rec* SkString::AllocRec(const char text[], size_t len) {
224 : Rec* rec;
225 :
226 0 : if (0 == len) {
227 0 : rec = const_cast<Rec*>(&gEmptyRec);
228 : } else {
229 0 : len = trim_size_t_to_u32(len);
230 :
231 : // add 1 for terminating 0, then align4 so we can have some slop when growing the string
232 0 : rec = (Rec*)sk_malloc_throw(SizeOfRec() + SkAlign4(len + 1));
233 0 : rec->fLength = SkToU32(len);
234 0 : rec->fRefCnt = 1;
235 0 : if (text) {
236 0 : memcpy(rec->data(), text, len);
237 : }
238 0 : rec->data()[len] = 0;
239 : }
240 0 : return rec;
241 : }
242 :
243 0 : SkString::Rec* SkString::RefRec(Rec* src) {
244 0 : if (src != &gEmptyRec) {
245 0 : sk_atomic_inc(&src->fRefCnt);
246 : }
247 0 : return src;
248 : }
249 :
250 : #ifdef SK_DEBUG
251 0 : void SkString::validate() const {
252 : // make sure know one has written over our global
253 0 : SkASSERT(0 == gEmptyRec.fLength);
254 0 : SkASSERT(0 == gEmptyRec.fRefCnt);
255 0 : SkASSERT(0 == gEmptyRec.data()[0]);
256 :
257 0 : if (fRec != &gEmptyRec) {
258 0 : SkASSERT(fRec->fLength > 0);
259 0 : SkASSERT(fRec->fRefCnt > 0);
260 0 : SkASSERT(0 == fRec->data()[fRec->fLength]);
261 : }
262 0 : }
263 : #endif
264 :
265 : ///////////////////////////////////////////////////////////////////////////////
266 :
267 0 : SkString::SkString() : fRec(const_cast<Rec*>(&gEmptyRec)) {
268 0 : }
269 :
270 0 : SkString::SkString(size_t len) {
271 0 : fRec = AllocRec(nullptr, len);
272 0 : }
273 :
274 0 : SkString::SkString(const char text[]) {
275 0 : size_t len = text ? strlen(text) : 0;
276 :
277 0 : fRec = AllocRec(text, len);
278 0 : }
279 :
280 0 : SkString::SkString(const char text[], size_t len) {
281 0 : fRec = AllocRec(text, len);
282 0 : }
283 :
284 0 : SkString::SkString(const SkString& src) {
285 0 : src.validate();
286 :
287 0 : fRec = RefRec(src.fRec);
288 0 : }
289 :
290 0 : SkString::SkString(SkString&& src) {
291 0 : src.validate();
292 :
293 0 : fRec = src.fRec;
294 0 : src.fRec = const_cast<Rec*>(&gEmptyRec);
295 0 : }
296 :
297 0 : SkString::~SkString() {
298 0 : this->validate();
299 :
300 0 : if (fRec->fLength) {
301 0 : SkASSERT(fRec->fRefCnt > 0);
302 0 : if (sk_atomic_dec(&fRec->fRefCnt) == 1) {
303 0 : sk_free(fRec);
304 : }
305 : }
306 0 : }
307 :
308 0 : bool SkString::equals(const SkString& src) const {
309 0 : return fRec == src.fRec || this->equals(src.c_str(), src.size());
310 : }
311 :
312 0 : bool SkString::equals(const char text[]) const {
313 0 : return this->equals(text, text ? strlen(text) : 0);
314 : }
315 :
316 0 : bool SkString::equals(const char text[], size_t len) const {
317 0 : SkASSERT(len == 0 || text != nullptr);
318 :
319 0 : return fRec->fLength == len && !memcmp(fRec->data(), text, len);
320 : }
321 :
322 0 : SkString& SkString::operator=(const SkString& src) {
323 0 : this->validate();
324 :
325 0 : if (fRec != src.fRec) {
326 0 : SkString tmp(src);
327 0 : this->swap(tmp);
328 : }
329 0 : return *this;
330 : }
331 :
332 0 : SkString& SkString::operator=(SkString&& src) {
333 0 : this->validate();
334 :
335 0 : if (fRec != src.fRec) {
336 0 : this->swap(src);
337 : }
338 0 : return *this;
339 : }
340 :
341 0 : SkString& SkString::operator=(const char text[]) {
342 0 : this->validate();
343 :
344 0 : SkString tmp(text);
345 0 : this->swap(tmp);
346 :
347 0 : return *this;
348 : }
349 :
350 0 : void SkString::reset() {
351 0 : this->validate();
352 :
353 0 : if (fRec->fLength) {
354 0 : SkASSERT(fRec->fRefCnt > 0);
355 0 : if (sk_atomic_dec(&fRec->fRefCnt) == 1) {
356 0 : sk_free(fRec);
357 : }
358 : }
359 :
360 0 : fRec = const_cast<Rec*>(&gEmptyRec);
361 0 : }
362 :
363 0 : char* SkString::writable_str() {
364 0 : this->validate();
365 :
366 0 : if (fRec->fLength) {
367 0 : if (fRec->fRefCnt > 1) {
368 0 : Rec* rec = AllocRec(fRec->data(), fRec->fLength);
369 0 : if (sk_atomic_dec(&fRec->fRefCnt) == 1) {
370 : // In this case after our check of fRecCnt > 1, we suddenly
371 : // did become the only owner, so now we have two copies of the
372 : // data (fRec and rec), so we need to delete one of them.
373 0 : sk_free(fRec);
374 : }
375 0 : fRec = rec;
376 : }
377 : }
378 0 : return fRec->data();
379 : }
380 :
381 0 : void SkString::set(const char text[]) {
382 0 : this->set(text, text ? strlen(text) : 0);
383 0 : }
384 :
385 0 : void SkString::set(const char text[], size_t len) {
386 0 : len = trim_size_t_to_u32(len);
387 :
388 0 : if (0 == len) {
389 0 : this->reset();
390 0 : } else if (1 == fRec->fRefCnt && len <= fRec->fLength) {
391 : // should we resize if len <<<< fLength, to save RAM? (e.g. len < (fLength>>1))?
392 : // just use less of the buffer without allocating a smaller one
393 0 : char* p = this->writable_str();
394 0 : if (text) {
395 0 : memcpy(p, text, len);
396 : }
397 0 : p[len] = 0;
398 0 : fRec->fLength = SkToU32(len);
399 0 : } else if (1 == fRec->fRefCnt && (fRec->fLength >> 2) == (len >> 2)) {
400 : // we have spare room in the current allocation, so don't alloc a larger one
401 0 : char* p = this->writable_str();
402 0 : if (text) {
403 0 : memcpy(p, text, len);
404 : }
405 0 : p[len] = 0;
406 0 : fRec->fLength = SkToU32(len);
407 : } else {
408 0 : SkString tmp(text, len);
409 0 : this->swap(tmp);
410 : }
411 0 : }
412 :
413 0 : void SkString::setUTF16(const uint16_t src[]) {
414 0 : int count = 0;
415 :
416 0 : while (src[count]) {
417 0 : count += 1;
418 : }
419 0 : this->setUTF16(src, count);
420 0 : }
421 :
422 0 : void SkString::setUTF16(const uint16_t src[], size_t count) {
423 0 : count = trim_size_t_to_u32(count);
424 :
425 0 : if (0 == count) {
426 0 : this->reset();
427 0 : } else if (count <= fRec->fLength) {
428 : // should we resize if len <<<< fLength, to save RAM? (e.g. len < (fLength>>1))
429 0 : if (count < fRec->fLength) {
430 0 : this->resize(count);
431 : }
432 0 : char* p = this->writable_str();
433 0 : for (size_t i = 0; i < count; i++) {
434 0 : p[i] = SkToU8(src[i]);
435 : }
436 0 : p[count] = 0;
437 : } else {
438 0 : SkString tmp(count); // puts a null terminator at the end of the string
439 0 : char* p = tmp.writable_str();
440 :
441 0 : for (size_t i = 0; i < count; i++) {
442 0 : p[i] = SkToU8(src[i]);
443 : }
444 0 : this->swap(tmp);
445 : }
446 0 : }
447 :
448 0 : void SkString::insert(size_t offset, const char text[]) {
449 0 : this->insert(offset, text, text ? strlen(text) : 0);
450 0 : }
451 :
452 0 : void SkString::insert(size_t offset, const char text[], size_t len) {
453 0 : if (len) {
454 0 : size_t length = fRec->fLength;
455 0 : if (offset > length) {
456 0 : offset = length;
457 : }
458 :
459 : // Check if length + len exceeds 32bits, we trim len
460 0 : len = check_add32(length, len);
461 0 : if (0 == len) {
462 0 : return;
463 : }
464 :
465 : /* If we're the only owner, and we have room in our allocation for the insert,
466 : do it in place, rather than allocating a new buffer.
467 :
468 : To know we have room, compare the allocated sizes
469 : beforeAlloc = SkAlign4(length + 1)
470 : afterAlloc = SkAligh4(length + 1 + len)
471 : but SkAlign4(x) is (x + 3) >> 2 << 2
472 : which is equivalent for testing to (length + 1 + 3) >> 2 == (length + 1 + 3 + len) >> 2
473 : and we can then eliminate the +1+3 since that doesn't affec the answer
474 : */
475 0 : if (1 == fRec->fRefCnt && (length >> 2) == ((length + len) >> 2)) {
476 0 : char* dst = this->writable_str();
477 :
478 0 : if (offset < length) {
479 0 : memmove(dst + offset + len, dst + offset, length - offset);
480 : }
481 0 : memcpy(dst + offset, text, len);
482 :
483 0 : dst[length + len] = 0;
484 0 : fRec->fLength = SkToU32(length + len);
485 : } else {
486 : /* Seems we should use realloc here, since that is safe if it fails
487 : (we have the original data), and might be faster than alloc/copy/free.
488 : */
489 0 : SkString tmp(fRec->fLength + len);
490 0 : char* dst = tmp.writable_str();
491 :
492 0 : if (offset > 0) {
493 0 : memcpy(dst, fRec->data(), offset);
494 : }
495 0 : memcpy(dst + offset, text, len);
496 0 : if (offset < fRec->fLength) {
497 0 : memcpy(dst + offset + len, fRec->data() + offset,
498 0 : fRec->fLength - offset);
499 : }
500 :
501 0 : this->swap(tmp);
502 : }
503 : }
504 : }
505 :
506 0 : void SkString::insertUnichar(size_t offset, SkUnichar uni) {
507 : char buffer[kMaxBytesInUTF8Sequence];
508 0 : size_t len = SkUTF8_FromUnichar(uni, buffer);
509 :
510 0 : if (len) {
511 0 : this->insert(offset, buffer, len);
512 : }
513 0 : }
514 :
515 0 : void SkString::insertS32(size_t offset, int32_t dec) {
516 : char buffer[SkStrAppendS32_MaxSize];
517 0 : char* stop = SkStrAppendS32(buffer, dec);
518 0 : this->insert(offset, buffer, stop - buffer);
519 0 : }
520 :
521 0 : void SkString::insertS64(size_t offset, int64_t dec, int minDigits) {
522 : char buffer[SkStrAppendS64_MaxSize];
523 0 : char* stop = SkStrAppendS64(buffer, dec, minDigits);
524 0 : this->insert(offset, buffer, stop - buffer);
525 0 : }
526 :
527 0 : void SkString::insertU32(size_t offset, uint32_t dec) {
528 : char buffer[SkStrAppendU32_MaxSize];
529 0 : char* stop = SkStrAppendU32(buffer, dec);
530 0 : this->insert(offset, buffer, stop - buffer);
531 0 : }
532 :
533 0 : void SkString::insertU64(size_t offset, uint64_t dec, int minDigits) {
534 : char buffer[SkStrAppendU64_MaxSize];
535 0 : char* stop = SkStrAppendU64(buffer, dec, minDigits);
536 0 : this->insert(offset, buffer, stop - buffer);
537 0 : }
538 :
539 0 : void SkString::insertHex(size_t offset, uint32_t hex, int minDigits) {
540 0 : minDigits = SkTPin(minDigits, 0, 8);
541 :
542 : static const char gHex[] = "0123456789ABCDEF";
543 :
544 : char buffer[8];
545 0 : char* p = buffer + sizeof(buffer);
546 :
547 0 : do {
548 0 : *--p = gHex[hex & 0xF];
549 0 : hex >>= 4;
550 0 : minDigits -= 1;
551 0 : } while (hex != 0);
552 :
553 0 : while (--minDigits >= 0) {
554 0 : *--p = '0';
555 : }
556 :
557 0 : SkASSERT(p >= buffer);
558 0 : this->insert(offset, p, buffer + sizeof(buffer) - p);
559 0 : }
560 :
561 0 : void SkString::insertScalar(size_t offset, SkScalar value) {
562 : char buffer[SkStrAppendScalar_MaxSize];
563 0 : char* stop = SkStrAppendScalar(buffer, value);
564 0 : this->insert(offset, buffer, stop - buffer);
565 0 : }
566 :
567 0 : void SkString::printf(const char format[], ...) {
568 0 : V_SKSTRING_PRINTF((*this), format);
569 0 : }
570 :
571 0 : void SkString::appendf(const char format[], ...) {
572 : char buffer[kBufferSize];
573 : int length;
574 0 : ARGS_TO_BUFFER(format, buffer, kBufferSize, length);
575 :
576 0 : this->append(buffer, length);
577 0 : }
578 :
579 0 : void SkString::appendVAList(const char format[], va_list args) {
580 : char buffer[kBufferSize];
581 0 : int length = VSNPRINTF(buffer, kBufferSize, format, args);
582 0 : SkASSERT(length >= 0 && length < SkToInt(kBufferSize));
583 :
584 0 : this->append(buffer, length);
585 0 : }
586 :
587 0 : void SkString::prependf(const char format[], ...) {
588 : char buffer[kBufferSize];
589 : int length;
590 0 : ARGS_TO_BUFFER(format, buffer, kBufferSize, length);
591 :
592 0 : this->prepend(buffer, length);
593 0 : }
594 :
595 0 : void SkString::prependVAList(const char format[], va_list args) {
596 : char buffer[kBufferSize];
597 0 : int length = VSNPRINTF(buffer, kBufferSize, format, args);
598 0 : SkASSERT(length >= 0 && length < SkToInt(kBufferSize));
599 :
600 0 : this->prepend(buffer, length);
601 0 : }
602 :
603 :
604 : ///////////////////////////////////////////////////////////////////////////////
605 :
606 0 : void SkString::remove(size_t offset, size_t length) {
607 0 : size_t size = this->size();
608 :
609 0 : if (offset < size) {
610 0 : if (length > size - offset) {
611 0 : length = size - offset;
612 : }
613 0 : SkASSERT(length <= size);
614 0 : SkASSERT(offset <= size - length);
615 0 : if (length > 0) {
616 0 : SkString tmp(size - length);
617 0 : char* dst = tmp.writable_str();
618 0 : const char* src = this->c_str();
619 :
620 0 : if (offset) {
621 0 : memcpy(dst, src, offset);
622 : }
623 0 : size_t tail = size - (offset + length);
624 0 : if (tail) {
625 0 : memcpy(dst + offset, src + (offset + length), tail);
626 : }
627 0 : SkASSERT(dst[tmp.size()] == 0);
628 0 : this->swap(tmp);
629 : }
630 : }
631 0 : }
632 :
633 0 : void SkString::swap(SkString& other) {
634 0 : this->validate();
635 0 : other.validate();
636 :
637 0 : SkTSwap<Rec*>(fRec, other.fRec);
638 0 : }
639 :
640 : ///////////////////////////////////////////////////////////////////////////////
641 :
642 0 : SkString SkStringPrintf(const char* format, ...) {
643 0 : SkString formattedOutput;
644 0 : V_SKSTRING_PRINTF(formattedOutput, format);
645 0 : return formattedOutput;
646 : }
647 :
648 0 : void SkStrSplit(const char* str, const char* delimiters, SkStrSplitMode splitMode,
649 : SkTArray<SkString>* out) {
650 0 : if (splitMode == kCoalesce_SkStrSplitMode) {
651 : // Skip any delimiters.
652 0 : str += strspn(str, delimiters);
653 : }
654 0 : if (!*str) {
655 0 : return;
656 : }
657 :
658 : while (true) {
659 : // Find a token.
660 0 : const size_t len = strcspn(str, delimiters);
661 0 : if (splitMode == kStrict_SkStrSplitMode || len > 0) {
662 0 : out->push_back().set(str, len);
663 0 : str += len;
664 : }
665 :
666 0 : if (!*str) {
667 0 : return;
668 : }
669 0 : if (splitMode == kCoalesce_SkStrSplitMode) {
670 : // Skip any delimiters.
671 0 : str += strspn(str, delimiters);
672 : } else {
673 : // Skip one delimiter.
674 0 : str += 1;
675 : }
676 0 : }
677 : }
678 :
679 : #undef VSNPRINTF
680 : #undef SNPRINTF
|