blob: 75daee9c2dd3f7dee76fec7a4c84f185e8bdadd5 [file] [log] [blame]
The Android Open Source Projectcbb10112009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2005 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <utils/String8.h>
18
19#include <utils/Log.h>
Kenny Rootba0165b2010-11-09 14:37:23 -080020#include <utils/Unicode.h>
21#include <utils/SharedBuffer.h>
The Android Open Source Projectcbb10112009-03-03 19:31:44 -080022#include <utils/String16.h>
23#include <utils/TextOutput.h>
24#include <utils/threads.h>
25
26#include <private/utils/Static.h>
27
28#include <ctype.h>
29
Daisuke Miyakawa44dad3e2009-06-30 20:40:42 +090030/*
31 * Functions outside android is below the namespace android, since they use
32 * functions and constants in android namespace.
33 */
The Android Open Source Projectcbb10112009-03-03 19:31:44 -080034
35// ---------------------------------------------------------------------------
36
Daisuke Miyakawa44dad3e2009-06-30 20:40:42 +090037namespace android {
38
The Android Open Source Projectcbb10112009-03-03 19:31:44 -080039// Separator used by resource paths. This is not platform dependent contrary
40// to OS_PATH_SEPARATOR.
41#define RES_PATH_SEPARATOR '/'
42
The Android Open Source Projectcbb10112009-03-03 19:31:44 -080043static SharedBuffer* gEmptyStringBuf = NULL;
44static char* gEmptyString = NULL;
45
46extern int gDarwinCantLoadAllObjects;
47int gDarwinIsReallyAnnoying;
48
49static inline char* getEmptyString()
50{
Todd Poynord9ad7d82013-05-02 15:41:35 -070051 if (!gEmptyStringBuf) initialize_string8();
52
The Android Open Source Projectcbb10112009-03-03 19:31:44 -080053 gEmptyStringBuf->acquire();
54 return gEmptyString;
55}
56
57void initialize_string8()
58{
Todd Poynord9ad7d82013-05-02 15:41:35 -070059 if (gEmptyStringBuf) return;
60
Dan Egnor88753ae2010-05-06 00:55:09 -070061 // HACK: This dummy dependency forces linking libutils Static.cpp,
62 // which is needed to initialize String8/String16 classes.
63 // These variables are named for Darwin, but are needed elsewhere too,
64 // including static linking on any platform.
65 gDarwinIsReallyAnnoying = gDarwinCantLoadAllObjects;
Daisuke Miyakawa44dad3e2009-06-30 20:40:42 +090066
The Android Open Source Projectcbb10112009-03-03 19:31:44 -080067 SharedBuffer* buf = SharedBuffer::alloc(1);
68 char* str = (char*)buf->data();
69 *str = 0;
70 gEmptyStringBuf = buf;
71 gEmptyString = str;
72}
73
74void terminate_string8()
75{
76 SharedBuffer::bufferFromData(gEmptyString)->release();
77 gEmptyStringBuf = NULL;
78 gEmptyString = NULL;
79}
80
81// ---------------------------------------------------------------------------
82
83static char* allocFromUTF8(const char* in, size_t len)
84{
85 if (len > 0) {
86 SharedBuffer* buf = SharedBuffer::alloc(len+1);
Steve Blockae074452012-01-09 18:35:44 +000087 ALOG_ASSERT(buf, "Unable to allocate shared buffer");
The Android Open Source Projectcbb10112009-03-03 19:31:44 -080088 if (buf) {
89 char* str = (char*)buf->data();
90 memcpy(str, in, len);
91 str[len] = 0;
92 return str;
93 }
94 return NULL;
95 }
96
97 return getEmptyString();
98}
99
Daisuke Miyakawa44dad3e2009-06-30 20:40:42 +0900100static char* allocFromUTF16(const char16_t* in, size_t len)
101{
Kenny Root9a2d83e2009-12-04 09:38:48 -0800102 if (len == 0) return getEmptyString();
103
Kenny Rootba0165b2010-11-09 14:37:23 -0800104 const ssize_t bytes = utf16_to_utf8_length(in, len);
105 if (bytes < 0) {
106 return getEmptyString();
107 }
Kenny Root9a2d83e2009-12-04 09:38:48 -0800108
109 SharedBuffer* buf = SharedBuffer::alloc(bytes+1);
Steve Blockae074452012-01-09 18:35:44 +0000110 ALOG_ASSERT(buf, "Unable to allocate shared buffer");
Kenny Rootba0165b2010-11-09 14:37:23 -0800111 if (!buf) {
112 return getEmptyString();
Kenny Root9a2d83e2009-12-04 09:38:48 -0800113 }
114
Kenny Rootba0165b2010-11-09 14:37:23 -0800115 char* str = (char*)buf->data();
116 utf16_to_utf8(in, len, str);
117 return str;
Daisuke Miyakawa44dad3e2009-06-30 20:40:42 +0900118}
119
120static char* allocFromUTF32(const char32_t* in, size_t len)
121{
Kenny Rootba0165b2010-11-09 14:37:23 -0800122 if (len == 0) {
123 return getEmptyString();
124 }
125
126 const ssize_t bytes = utf32_to_utf8_length(in, len);
127 if (bytes < 0) {
128 return getEmptyString();
129 }
130
131 SharedBuffer* buf = SharedBuffer::alloc(bytes+1);
Steve Blockae074452012-01-09 18:35:44 +0000132 ALOG_ASSERT(buf, "Unable to allocate shared buffer");
Kenny Rootba0165b2010-11-09 14:37:23 -0800133 if (!buf) {
134 return getEmptyString();
135 }
136
137 char* str = (char*) buf->data();
138 utf32_to_utf8(in, len, str);
139
140 return str;
Daisuke Miyakawa44dad3e2009-06-30 20:40:42 +0900141}
142
The Android Open Source Projectcbb10112009-03-03 19:31:44 -0800143// ---------------------------------------------------------------------------
144
145String8::String8()
146 : mString(getEmptyString())
147{
148}
149
150String8::String8(const String8& o)
151 : mString(o.mString)
152{
153 SharedBuffer::bufferFromData(mString)->acquire();
154}
155
156String8::String8(const char* o)
157 : mString(allocFromUTF8(o, strlen(o)))
158{
159 if (mString == NULL) {
160 mString = getEmptyString();
161 }
162}
163
164String8::String8(const char* o, size_t len)
165 : mString(allocFromUTF8(o, len))
166{
167 if (mString == NULL) {
168 mString = getEmptyString();
169 }
170}
171
172String8::String8(const String16& o)
173 : mString(allocFromUTF16(o.string(), o.size()))
174{
175}
176
177String8::String8(const char16_t* o)
178 : mString(allocFromUTF16(o, strlen16(o)))
179{
180}
181
182String8::String8(const char16_t* o, size_t len)
183 : mString(allocFromUTF16(o, len))
184{
185}
186
Daisuke Miyakawa44dad3e2009-06-30 20:40:42 +0900187String8::String8(const char32_t* o)
188 : mString(allocFromUTF32(o, strlen32(o)))
189{
190}
191
192String8::String8(const char32_t* o, size_t len)
193 : mString(allocFromUTF32(o, len))
194{
195}
196
The Android Open Source Projectcbb10112009-03-03 19:31:44 -0800197String8::~String8()
198{
199 SharedBuffer::bufferFromData(mString)->release();
200}
201
Jeff Brown1d618d62010-12-02 13:50:46 -0800202String8 String8::format(const char* fmt, ...)
203{
204 va_list args;
205 va_start(args, fmt);
206
207 String8 result(formatV(fmt, args));
208
209 va_end(args);
210 return result;
211}
212
213String8 String8::formatV(const char* fmt, va_list args)
214{
215 String8 result;
216 result.appendFormatV(fmt, args);
217 return result;
218}
219
Jeff Brown48da31b2010-09-12 17:55:08 -0700220void String8::clear() {
221 SharedBuffer::bufferFromData(mString)->release();
222 mString = getEmptyString();
223}
224
The Android Open Source Projectcbb10112009-03-03 19:31:44 -0800225void String8::setTo(const String8& other)
226{
227 SharedBuffer::bufferFromData(other.mString)->acquire();
228 SharedBuffer::bufferFromData(mString)->release();
229 mString = other.mString;
230}
231
232status_t String8::setTo(const char* other)
233{
Andreas Huber10e5da52010-06-10 11:14:26 -0700234 const char *newString = allocFromUTF8(other, strlen(other));
The Android Open Source Projectcbb10112009-03-03 19:31:44 -0800235 SharedBuffer::bufferFromData(mString)->release();
Andreas Huber10e5da52010-06-10 11:14:26 -0700236 mString = newString;
The Android Open Source Projectcbb10112009-03-03 19:31:44 -0800237 if (mString) return NO_ERROR;
238
239 mString = getEmptyString();
240 return NO_MEMORY;
241}
242
243status_t String8::setTo(const char* other, size_t len)
244{
Andreas Huber10e5da52010-06-10 11:14:26 -0700245 const char *newString = allocFromUTF8(other, len);
The Android Open Source Projectcbb10112009-03-03 19:31:44 -0800246 SharedBuffer::bufferFromData(mString)->release();
Andreas Huber10e5da52010-06-10 11:14:26 -0700247 mString = newString;
The Android Open Source Projectcbb10112009-03-03 19:31:44 -0800248 if (mString) return NO_ERROR;
249
250 mString = getEmptyString();
251 return NO_MEMORY;
252}
253
254status_t String8::setTo(const char16_t* other, size_t len)
255{
Andreas Huber10e5da52010-06-10 11:14:26 -0700256 const char *newString = allocFromUTF16(other, len);
The Android Open Source Projectcbb10112009-03-03 19:31:44 -0800257 SharedBuffer::bufferFromData(mString)->release();
Andreas Huber10e5da52010-06-10 11:14:26 -0700258 mString = newString;
The Android Open Source Projectcbb10112009-03-03 19:31:44 -0800259 if (mString) return NO_ERROR;
260
261 mString = getEmptyString();
262 return NO_MEMORY;
263}
264
Daisuke Miyakawa44dad3e2009-06-30 20:40:42 +0900265status_t String8::setTo(const char32_t* other, size_t len)
266{
Andreas Huber10e5da52010-06-10 11:14:26 -0700267 const char *newString = allocFromUTF32(other, len);
Daisuke Miyakawa44dad3e2009-06-30 20:40:42 +0900268 SharedBuffer::bufferFromData(mString)->release();
Andreas Huber10e5da52010-06-10 11:14:26 -0700269 mString = newString;
Daisuke Miyakawa44dad3e2009-06-30 20:40:42 +0900270 if (mString) return NO_ERROR;
271
272 mString = getEmptyString();
273 return NO_MEMORY;
274}
275
The Android Open Source Projectcbb10112009-03-03 19:31:44 -0800276status_t String8::append(const String8& other)
277{
278 const size_t otherLen = other.bytes();
279 if (bytes() == 0) {
280 setTo(other);
281 return NO_ERROR;
282 } else if (otherLen == 0) {
283 return NO_ERROR;
284 }
285
286 return real_append(other.string(), otherLen);
287}
288
289status_t String8::append(const char* other)
290{
291 return append(other, strlen(other));
292}
293
294status_t String8::append(const char* other, size_t otherLen)
295{
296 if (bytes() == 0) {
297 return setTo(other, otherLen);
298 } else if (otherLen == 0) {
299 return NO_ERROR;
300 }
301
302 return real_append(other, otherLen);
303}
304
Jeff Brown35a154e2010-07-15 23:54:05 -0700305status_t String8::appendFormat(const char* fmt, ...)
306{
Jeff Brown647925d2010-11-10 16:03:06 -0800307 va_list args;
308 va_start(args, fmt);
Jeff Brown35a154e2010-07-15 23:54:05 -0700309
Jeff Brown647925d2010-11-10 16:03:06 -0800310 status_t result = appendFormatV(fmt, args);
311
312 va_end(args);
313 return result;
314}
315
316status_t String8::appendFormatV(const char* fmt, va_list args)
317{
Jeff Brown35a154e2010-07-15 23:54:05 -0700318 int result = NO_ERROR;
Jeff Brown647925d2010-11-10 16:03:06 -0800319 int n = vsnprintf(NULL, 0, fmt, args);
Jeff Brown35a154e2010-07-15 23:54:05 -0700320 if (n != 0) {
321 size_t oldLength = length();
322 char* buf = lockBuffer(oldLength + n);
323 if (buf) {
Jeff Brown647925d2010-11-10 16:03:06 -0800324 vsnprintf(buf + oldLength, n + 1, fmt, args);
Jeff Brown35a154e2010-07-15 23:54:05 -0700325 } else {
326 result = NO_MEMORY;
327 }
328 }
Jeff Brown35a154e2010-07-15 23:54:05 -0700329 return result;
330}
331
The Android Open Source Projectcbb10112009-03-03 19:31:44 -0800332status_t String8::real_append(const char* other, size_t otherLen)
333{
334 const size_t myLen = bytes();
335
336 SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
337 ->editResize(myLen+otherLen+1);
338 if (buf) {
339 char* str = (char*)buf->data();
340 mString = str;
341 str += myLen;
342 memcpy(str, other, otherLen);
343 str[otherLen] = '\0';
344 return NO_ERROR;
345 }
346 return NO_MEMORY;
347}
348
349char* String8::lockBuffer(size_t size)
350{
351 SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
352 ->editResize(size+1);
353 if (buf) {
354 char* str = (char*)buf->data();
355 mString = str;
356 return str;
357 }
358 return NULL;
359}
360
361void String8::unlockBuffer()
362{
363 unlockBuffer(strlen(mString));
364}
365
366status_t String8::unlockBuffer(size_t size)
367{
368 if (size != this->size()) {
369 SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
370 ->editResize(size+1);
Jeff Brown35a154e2010-07-15 23:54:05 -0700371 if (! buf) {
372 return NO_MEMORY;
The Android Open Source Projectcbb10112009-03-03 19:31:44 -0800373 }
Jeff Brown35a154e2010-07-15 23:54:05 -0700374
375 char* str = (char*)buf->data();
376 str[size] = 0;
377 mString = str;
The Android Open Source Projectcbb10112009-03-03 19:31:44 -0800378 }
Jeff Brown35a154e2010-07-15 23:54:05 -0700379
380 return NO_ERROR;
The Android Open Source Projectcbb10112009-03-03 19:31:44 -0800381}
382
383ssize_t String8::find(const char* other, size_t start) const
384{
385 size_t len = size();
386 if (start >= len) {
387 return -1;
388 }
389 const char* s = mString+start;
390 const char* p = strstr(s, other);
391 return p ? p-mString : -1;
392}
393
394void String8::toLower()
395{
396 toLower(0, size());
397}
398
399void String8::toLower(size_t start, size_t length)
400{
401 const size_t len = size();
402 if (start >= len) {
403 return;
404 }
405 if (start+length > len) {
406 length = len-start;
407 }
408 char* buf = lockBuffer(len);
409 buf += start;
410 while (length > 0) {
411 *buf = tolower(*buf);
412 buf++;
413 length--;
414 }
415 unlockBuffer(len);
416}
417
418void String8::toUpper()
419{
420 toUpper(0, size());
421}
422
423void String8::toUpper(size_t start, size_t length)
424{
425 const size_t len = size();
426 if (start >= len) {
427 return;
428 }
429 if (start+length > len) {
430 length = len-start;
431 }
432 char* buf = lockBuffer(len);
433 buf += start;
434 while (length > 0) {
435 *buf = toupper(*buf);
436 buf++;
437 length--;
438 }
439 unlockBuffer(len);
440}
441
Daisuke Miyakawa44dad3e2009-06-30 20:40:42 +0900442size_t String8::getUtf32Length() const
443{
Kenny Rootba0165b2010-11-09 14:37:23 -0800444 return utf8_to_utf32_length(mString, length());
Daisuke Miyakawa44dad3e2009-06-30 20:40:42 +0900445}
446
447int32_t String8::getUtf32At(size_t index, size_t *next_index) const
448{
Kenny Rootba0165b2010-11-09 14:37:23 -0800449 return utf32_from_utf8_at(mString, length(), index, next_index);
Daisuke Miyakawa44dad3e2009-06-30 20:40:42 +0900450}
451
Kenny Rootba0165b2010-11-09 14:37:23 -0800452void String8::getUtf32(char32_t* dst) const
Daisuke Miyakawa44dad3e2009-06-30 20:40:42 +0900453{
Kenny Rootba0165b2010-11-09 14:37:23 -0800454 utf8_to_utf32(mString, length(), dst);
Daisuke Miyakawa44dad3e2009-06-30 20:40:42 +0900455}
456
The Android Open Source Projectcbb10112009-03-03 19:31:44 -0800457TextOutput& operator<<(TextOutput& to, const String8& val)
458{
459 to << val.string();
460 return to;
461}
462
463// ---------------------------------------------------------------------------
464// Path functions
465
The Android Open Source Projectcbb10112009-03-03 19:31:44 -0800466void String8::setPathName(const char* name)
467{
468 setPathName(name, strlen(name));
469}
470
471void String8::setPathName(const char* name, size_t len)
472{
473 char* buf = lockBuffer(len);
474
475 memcpy(buf, name, len);
476
477 // remove trailing path separator, if present
478 if (len > 0 && buf[len-1] == OS_PATH_SEPARATOR)
479 len--;
480
481 buf[len] = '\0';
482
483 unlockBuffer(len);
484}
485
486String8 String8::getPathLeaf(void) const
487{
488 const char* cp;
489 const char*const buf = mString;
490
491 cp = strrchr(buf, OS_PATH_SEPARATOR);
492 if (cp == NULL)
493 return String8(*this);
494 else
495 return String8(cp+1);
496}
497
498String8 String8::getPathDir(void) const
499{
500 const char* cp;
501 const char*const str = mString;
502
503 cp = strrchr(str, OS_PATH_SEPARATOR);
504 if (cp == NULL)
505 return String8("");
506 else
507 return String8(str, cp - str);
508}
509
510String8 String8::walkPath(String8* outRemains) const
511{
512 const char* cp;
513 const char*const str = mString;
514 const char* buf = str;
515
516 cp = strchr(buf, OS_PATH_SEPARATOR);
517 if (cp == buf) {
518 // don't include a leading '/'.
519 buf = buf+1;
520 cp = strchr(buf, OS_PATH_SEPARATOR);
521 }
522
523 if (cp == NULL) {
524 String8 res = buf != str ? String8(buf) : *this;
525 if (outRemains) *outRemains = String8("");
526 return res;
527 }
528
529 String8 res(buf, cp-buf);
530 if (outRemains) *outRemains = String8(cp+1);
531 return res;
532}
533
534/*
535 * Helper function for finding the start of an extension in a pathname.
536 *
537 * Returns a pointer inside mString, or NULL if no extension was found.
538 */
539char* String8::find_extension(void) const
540{
541 const char* lastSlash;
542 const char* lastDot;
543 int extLen;
544 const char* const str = mString;
545
546 // only look at the filename
547 lastSlash = strrchr(str, OS_PATH_SEPARATOR);
548 if (lastSlash == NULL)
549 lastSlash = str;
550 else
551 lastSlash++;
552
553 // find the last dot
554 lastDot = strrchr(lastSlash, '.');
555 if (lastDot == NULL)
556 return NULL;
557
558 // looks good, ship it
559 return const_cast<char*>(lastDot);
560}
561
562String8 String8::getPathExtension(void) const
563{
564 char* ext;
565
566 ext = find_extension();
567 if (ext != NULL)
568 return String8(ext);
569 else
570 return String8("");
571}
572
573String8 String8::getBasePath(void) const
574{
575 char* ext;
576 const char* const str = mString;
577
578 ext = find_extension();
579 if (ext == NULL)
580 return String8(*this);
581 else
582 return String8(str, ext - str);
583}
584
585String8& String8::appendPath(const char* name)
586{
587 // TODO: The test below will fail for Win32 paths. Fix later or ignore.
588 if (name[0] != OS_PATH_SEPARATOR) {
589 if (*name == '\0') {
590 // nothing to do
591 return *this;
592 }
593
594 size_t len = length();
595 if (len == 0) {
596 // no existing filename, just use the new one
597 setPathName(name);
598 return *this;
599 }
600
601 // make room for oldPath + '/' + newPath
602 int newlen = strlen(name);
603
604 char* buf = lockBuffer(len+1+newlen);
605
606 // insert a '/' if needed
607 if (buf[len-1] != OS_PATH_SEPARATOR)
608 buf[len++] = OS_PATH_SEPARATOR;
609
610 memcpy(buf+len, name, newlen+1);
611 len += newlen;
612
613 unlockBuffer(len);
614
615 return *this;
616 } else {
617 setPathName(name);
618 return *this;
619 }
620}
621
622String8& String8::convertToResPath()
623{
624#if OS_PATH_SEPARATOR != RES_PATH_SEPARATOR
625 size_t len = length();
626 if (len > 0) {
627 char * buf = lockBuffer(len);
628 for (char * end = buf + len; buf < end; ++buf) {
629 if (*buf == OS_PATH_SEPARATOR)
630 *buf = RES_PATH_SEPARATOR;
631 }
632 unlockBuffer(len);
633 }
634#endif
635 return *this;
636}
637
The Android Open Source Projectcbb10112009-03-03 19:31:44 -0800638}; // namespace android