diff options
Diffstat (limited to 'libutils/String8.cpp')
-rw-r--r-- | libutils/String8.cpp | 640 |
1 files changed, 640 insertions, 0 deletions
diff --git a/libutils/String8.cpp b/libutils/String8.cpp new file mode 100644 index 000000000..e852d77b7 --- /dev/null +++ b/libutils/String8.cpp | |||
@@ -0,0 +1,640 @@ | |||
1 | /* | ||
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> | ||
20 | #include <utils/Unicode.h> | ||
21 | #include <utils/SharedBuffer.h> | ||
22 | #include <utils/String16.h> | ||
23 | #include <utils/threads.h> | ||
24 | |||
25 | #include <ctype.h> | ||
26 | |||
27 | /* | ||
28 | * Functions outside android is below the namespace android, since they use | ||
29 | * functions and constants in android namespace. | ||
30 | */ | ||
31 | |||
32 | // --------------------------------------------------------------------------- | ||
33 | |||
34 | namespace android { | ||
35 | |||
36 | // Separator used by resource paths. This is not platform dependent contrary | ||
37 | // to OS_PATH_SEPARATOR. | ||
38 | #define RES_PATH_SEPARATOR '/' | ||
39 | |||
40 | static SharedBuffer* gEmptyStringBuf = NULL; | ||
41 | static char* gEmptyString = NULL; | ||
42 | |||
43 | extern int gDarwinCantLoadAllObjects; | ||
44 | int gDarwinIsReallyAnnoying; | ||
45 | |||
46 | void initialize_string8(); | ||
47 | |||
48 | static inline char* getEmptyString() | ||
49 | { | ||
50 | gEmptyStringBuf->acquire(); | ||
51 | return gEmptyString; | ||
52 | } | ||
53 | |||
54 | void initialize_string8() | ||
55 | { | ||
56 | // HACK: This dummy dependency forces linking libutils Static.cpp, | ||
57 | // which is needed to initialize String8/String16 classes. | ||
58 | // These variables are named for Darwin, but are needed elsewhere too, | ||
59 | // including static linking on any platform. | ||
60 | gDarwinIsReallyAnnoying = gDarwinCantLoadAllObjects; | ||
61 | |||
62 | SharedBuffer* buf = SharedBuffer::alloc(1); | ||
63 | char* str = (char*)buf->data(); | ||
64 | *str = 0; | ||
65 | gEmptyStringBuf = buf; | ||
66 | gEmptyString = str; | ||
67 | } | ||
68 | |||
69 | void terminate_string8() | ||
70 | { | ||
71 | SharedBuffer::bufferFromData(gEmptyString)->release(); | ||
72 | gEmptyStringBuf = NULL; | ||
73 | gEmptyString = NULL; | ||
74 | } | ||
75 | |||
76 | // --------------------------------------------------------------------------- | ||
77 | |||
78 | static char* allocFromUTF8(const char* in, size_t len) | ||
79 | { | ||
80 | if (len > 0) { | ||
81 | SharedBuffer* buf = SharedBuffer::alloc(len+1); | ||
82 | ALOG_ASSERT(buf, "Unable to allocate shared buffer"); | ||
83 | if (buf) { | ||
84 | char* str = (char*)buf->data(); | ||
85 | memcpy(str, in, len); | ||
86 | str[len] = 0; | ||
87 | return str; | ||
88 | } | ||
89 | return NULL; | ||
90 | } | ||
91 | |||
92 | return getEmptyString(); | ||
93 | } | ||
94 | |||
95 | static char* allocFromUTF16(const char16_t* in, size_t len) | ||
96 | { | ||
97 | if (len == 0) return getEmptyString(); | ||
98 | |||
99 | const ssize_t bytes = utf16_to_utf8_length(in, len); | ||
100 | if (bytes < 0) { | ||
101 | return getEmptyString(); | ||
102 | } | ||
103 | |||
104 | SharedBuffer* buf = SharedBuffer::alloc(bytes+1); | ||
105 | ALOG_ASSERT(buf, "Unable to allocate shared buffer"); | ||
106 | if (!buf) { | ||
107 | return getEmptyString(); | ||
108 | } | ||
109 | |||
110 | char* str = (char*)buf->data(); | ||
111 | utf16_to_utf8(in, len, str); | ||
112 | return str; | ||
113 | } | ||
114 | |||
115 | static char* allocFromUTF32(const char32_t* in, size_t len) | ||
116 | { | ||
117 | if (len == 0) { | ||
118 | return getEmptyString(); | ||
119 | } | ||
120 | |||
121 | const ssize_t bytes = utf32_to_utf8_length(in, len); | ||
122 | if (bytes < 0) { | ||
123 | return getEmptyString(); | ||
124 | } | ||
125 | |||
126 | SharedBuffer* buf = SharedBuffer::alloc(bytes+1); | ||
127 | ALOG_ASSERT(buf, "Unable to allocate shared buffer"); | ||
128 | if (!buf) { | ||
129 | return getEmptyString(); | ||
130 | } | ||
131 | |||
132 | char* str = (char*) buf->data(); | ||
133 | utf32_to_utf8(in, len, str); | ||
134 | |||
135 | return str; | ||
136 | } | ||
137 | |||
138 | // --------------------------------------------------------------------------- | ||
139 | |||
140 | String8::String8() | ||
141 | : mString(getEmptyString()) | ||
142 | { | ||
143 | } | ||
144 | |||
145 | String8::String8(StaticLinkage) | ||
146 | : mString(0) | ||
147 | { | ||
148 | // this constructor is used when we can't rely on the static-initializers | ||
149 | // having run. In this case we always allocate an empty string. It's less | ||
150 | // efficient than using getEmptyString(), but we assume it's uncommon. | ||
151 | |||
152 | char* data = static_cast<char*>( | ||
153 | SharedBuffer::alloc(sizeof(char))->data()); | ||
154 | data[0] = 0; | ||
155 | mString = data; | ||
156 | } | ||
157 | |||
158 | String8::String8(const String8& o) | ||
159 | : mString(o.mString) | ||
160 | { | ||
161 | SharedBuffer::bufferFromData(mString)->acquire(); | ||
162 | } | ||
163 | |||
164 | String8::String8(const char* o) | ||
165 | : mString(allocFromUTF8(o, strlen(o))) | ||
166 | { | ||
167 | if (mString == NULL) { | ||
168 | mString = getEmptyString(); | ||
169 | } | ||
170 | } | ||
171 | |||
172 | String8::String8(const char* o, size_t len) | ||
173 | : mString(allocFromUTF8(o, len)) | ||
174 | { | ||
175 | if (mString == NULL) { | ||
176 | mString = getEmptyString(); | ||
177 | } | ||
178 | } | ||
179 | |||
180 | String8::String8(const String16& o) | ||
181 | : mString(allocFromUTF16(o.string(), o.size())) | ||
182 | { | ||
183 | } | ||
184 | |||
185 | String8::String8(const char16_t* o) | ||
186 | : mString(allocFromUTF16(o, strlen16(o))) | ||
187 | { | ||
188 | } | ||
189 | |||
190 | String8::String8(const char16_t* o, size_t len) | ||
191 | : mString(allocFromUTF16(o, len)) | ||
192 | { | ||
193 | } | ||
194 | |||
195 | String8::String8(const char32_t* o) | ||
196 | : mString(allocFromUTF32(o, strlen32(o))) | ||
197 | { | ||
198 | } | ||
199 | |||
200 | String8::String8(const char32_t* o, size_t len) | ||
201 | : mString(allocFromUTF32(o, len)) | ||
202 | { | ||
203 | } | ||
204 | |||
205 | String8::~String8() | ||
206 | { | ||
207 | SharedBuffer::bufferFromData(mString)->release(); | ||
208 | } | ||
209 | |||
210 | String8 String8::format(const char* fmt, ...) | ||
211 | { | ||
212 | va_list args; | ||
213 | va_start(args, fmt); | ||
214 | |||
215 | String8 result(formatV(fmt, args)); | ||
216 | |||
217 | va_end(args); | ||
218 | return result; | ||
219 | } | ||
220 | |||
221 | String8 String8::formatV(const char* fmt, va_list args) | ||
222 | { | ||
223 | String8 result; | ||
224 | result.appendFormatV(fmt, args); | ||
225 | return result; | ||
226 | } | ||
227 | |||
228 | void String8::clear() { | ||
229 | SharedBuffer::bufferFromData(mString)->release(); | ||
230 | mString = getEmptyString(); | ||
231 | } | ||
232 | |||
233 | void String8::setTo(const String8& other) | ||
234 | { | ||
235 | SharedBuffer::bufferFromData(other.mString)->acquire(); | ||
236 | SharedBuffer::bufferFromData(mString)->release(); | ||
237 | mString = other.mString; | ||
238 | } | ||
239 | |||
240 | status_t String8::setTo(const char* other) | ||
241 | { | ||
242 | const char *newString = allocFromUTF8(other, strlen(other)); | ||
243 | SharedBuffer::bufferFromData(mString)->release(); | ||
244 | mString = newString; | ||
245 | if (mString) return NO_ERROR; | ||
246 | |||
247 | mString = getEmptyString(); | ||
248 | return NO_MEMORY; | ||
249 | } | ||
250 | |||
251 | status_t String8::setTo(const char* other, size_t len) | ||
252 | { | ||
253 | const char *newString = allocFromUTF8(other, len); | ||
254 | SharedBuffer::bufferFromData(mString)->release(); | ||
255 | mString = newString; | ||
256 | if (mString) return NO_ERROR; | ||
257 | |||
258 | mString = getEmptyString(); | ||
259 | return NO_MEMORY; | ||
260 | } | ||
261 | |||
262 | status_t String8::setTo(const char16_t* other, size_t len) | ||
263 | { | ||
264 | const char *newString = allocFromUTF16(other, len); | ||
265 | SharedBuffer::bufferFromData(mString)->release(); | ||
266 | mString = newString; | ||
267 | if (mString) return NO_ERROR; | ||
268 | |||
269 | mString = getEmptyString(); | ||
270 | return NO_MEMORY; | ||
271 | } | ||
272 | |||
273 | status_t String8::setTo(const char32_t* other, size_t len) | ||
274 | { | ||
275 | const char *newString = allocFromUTF32(other, len); | ||
276 | SharedBuffer::bufferFromData(mString)->release(); | ||
277 | mString = newString; | ||
278 | if (mString) return NO_ERROR; | ||
279 | |||
280 | mString = getEmptyString(); | ||
281 | return NO_MEMORY; | ||
282 | } | ||
283 | |||
284 | status_t String8::append(const String8& other) | ||
285 | { | ||
286 | const size_t otherLen = other.bytes(); | ||
287 | if (bytes() == 0) { | ||
288 | setTo(other); | ||
289 | return NO_ERROR; | ||
290 | } else if (otherLen == 0) { | ||
291 | return NO_ERROR; | ||
292 | } | ||
293 | |||
294 | return real_append(other.string(), otherLen); | ||
295 | } | ||
296 | |||
297 | status_t String8::append(const char* other) | ||
298 | { | ||
299 | return append(other, strlen(other)); | ||
300 | } | ||
301 | |||
302 | status_t String8::append(const char* other, size_t otherLen) | ||
303 | { | ||
304 | if (bytes() == 0) { | ||
305 | return setTo(other, otherLen); | ||
306 | } else if (otherLen == 0) { | ||
307 | return NO_ERROR; | ||
308 | } | ||
309 | |||
310 | return real_append(other, otherLen); | ||
311 | } | ||
312 | |||
313 | status_t String8::appendFormat(const char* fmt, ...) | ||
314 | { | ||
315 | va_list args; | ||
316 | va_start(args, fmt); | ||
317 | |||
318 | status_t result = appendFormatV(fmt, args); | ||
319 | |||
320 | va_end(args); | ||
321 | return result; | ||
322 | } | ||
323 | |||
324 | status_t String8::appendFormatV(const char* fmt, va_list args) | ||
325 | { | ||
326 | int result = NO_ERROR; | ||
327 | int n = vsnprintf(NULL, 0, fmt, args); | ||
328 | if (n != 0) { | ||
329 | size_t oldLength = length(); | ||
330 | char* buf = lockBuffer(oldLength + n); | ||
331 | if (buf) { | ||
332 | vsnprintf(buf + oldLength, n + 1, fmt, args); | ||
333 | } else { | ||
334 | result = NO_MEMORY; | ||
335 | } | ||
336 | } | ||
337 | return result; | ||
338 | } | ||
339 | |||
340 | status_t String8::real_append(const char* other, size_t otherLen) | ||
341 | { | ||
342 | const size_t myLen = bytes(); | ||
343 | |||
344 | SharedBuffer* buf = SharedBuffer::bufferFromData(mString) | ||
345 | ->editResize(myLen+otherLen+1); | ||
346 | if (buf) { | ||
347 | char* str = (char*)buf->data(); | ||
348 | mString = str; | ||
349 | str += myLen; | ||
350 | memcpy(str, other, otherLen); | ||
351 | str[otherLen] = '\0'; | ||
352 | return NO_ERROR; | ||
353 | } | ||
354 | return NO_MEMORY; | ||
355 | } | ||
356 | |||
357 | char* String8::lockBuffer(size_t size) | ||
358 | { | ||
359 | SharedBuffer* buf = SharedBuffer::bufferFromData(mString) | ||
360 | ->editResize(size+1); | ||
361 | if (buf) { | ||
362 | char* str = (char*)buf->data(); | ||
363 | mString = str; | ||
364 | return str; | ||
365 | } | ||
366 | return NULL; | ||
367 | } | ||
368 | |||
369 | void String8::unlockBuffer() | ||
370 | { | ||
371 | unlockBuffer(strlen(mString)); | ||
372 | } | ||
373 | |||
374 | status_t String8::unlockBuffer(size_t size) | ||
375 | { | ||
376 | if (size != this->size()) { | ||
377 | SharedBuffer* buf = SharedBuffer::bufferFromData(mString) | ||
378 | ->editResize(size+1); | ||
379 | if (! buf) { | ||
380 | return NO_MEMORY; | ||
381 | } | ||
382 | |||
383 | char* str = (char*)buf->data(); | ||
384 | str[size] = 0; | ||
385 | mString = str; | ||
386 | } | ||
387 | |||
388 | return NO_ERROR; | ||
389 | } | ||
390 | |||
391 | ssize_t String8::find(const char* other, size_t start) const | ||
392 | { | ||
393 | size_t len = size(); | ||
394 | if (start >= len) { | ||
395 | return -1; | ||
396 | } | ||
397 | const char* s = mString+start; | ||
398 | const char* p = strstr(s, other); | ||
399 | return p ? p-mString : -1; | ||
400 | } | ||
401 | |||
402 | void String8::toLower() | ||
403 | { | ||
404 | toLower(0, size()); | ||
405 | } | ||
406 | |||
407 | void String8::toLower(size_t start, size_t length) | ||
408 | { | ||
409 | const size_t len = size(); | ||
410 | if (start >= len) { | ||
411 | return; | ||
412 | } | ||
413 | if (start+length > len) { | ||
414 | length = len-start; | ||
415 | } | ||
416 | char* buf = lockBuffer(len); | ||
417 | buf += start; | ||
418 | while (length > 0) { | ||
419 | *buf = tolower(*buf); | ||
420 | buf++; | ||
421 | length--; | ||
422 | } | ||
423 | unlockBuffer(len); | ||
424 | } | ||
425 | |||
426 | void String8::toUpper() | ||
427 | { | ||
428 | toUpper(0, size()); | ||
429 | } | ||
430 | |||
431 | void String8::toUpper(size_t start, size_t length) | ||
432 | { | ||
433 | const size_t len = size(); | ||
434 | if (start >= len) { | ||
435 | return; | ||
436 | } | ||
437 | if (start+length > len) { | ||
438 | length = len-start; | ||
439 | } | ||
440 | char* buf = lockBuffer(len); | ||
441 | buf += start; | ||
442 | while (length > 0) { | ||
443 | *buf = toupper(*buf); | ||
444 | buf++; | ||
445 | length--; | ||
446 | } | ||
447 | unlockBuffer(len); | ||
448 | } | ||
449 | |||
450 | size_t String8::getUtf32Length() const | ||
451 | { | ||
452 | return utf8_to_utf32_length(mString, length()); | ||
453 | } | ||
454 | |||
455 | int32_t String8::getUtf32At(size_t index, size_t *next_index) const | ||
456 | { | ||
457 | return utf32_from_utf8_at(mString, length(), index, next_index); | ||
458 | } | ||
459 | |||
460 | void String8::getUtf32(char32_t* dst) const | ||
461 | { | ||
462 | utf8_to_utf32(mString, length(), dst); | ||
463 | } | ||
464 | |||
465 | // --------------------------------------------------------------------------- | ||
466 | // Path functions | ||
467 | |||
468 | void String8::setPathName(const char* name) | ||
469 | { | ||
470 | setPathName(name, strlen(name)); | ||
471 | } | ||
472 | |||
473 | void String8::setPathName(const char* name, size_t len) | ||
474 | { | ||
475 | char* buf = lockBuffer(len); | ||
476 | |||
477 | memcpy(buf, name, len); | ||
478 | |||
479 | // remove trailing path separator, if present | ||
480 | if (len > 0 && buf[len-1] == OS_PATH_SEPARATOR) | ||
481 | len--; | ||
482 | |||
483 | buf[len] = '\0'; | ||
484 | |||
485 | unlockBuffer(len); | ||
486 | } | ||
487 | |||
488 | String8 String8::getPathLeaf(void) const | ||
489 | { | ||
490 | const char* cp; | ||
491 | const char*const buf = mString; | ||
492 | |||
493 | cp = strrchr(buf, OS_PATH_SEPARATOR); | ||
494 | if (cp == NULL) | ||
495 | return String8(*this); | ||
496 | else | ||
497 | return String8(cp+1); | ||
498 | } | ||
499 | |||
500 | String8 String8::getPathDir(void) const | ||
501 | { | ||
502 | const char* cp; | ||
503 | const char*const str = mString; | ||
504 | |||
505 | cp = strrchr(str, OS_PATH_SEPARATOR); | ||
506 | if (cp == NULL) | ||
507 | return String8(""); | ||
508 | else | ||
509 | return String8(str, cp - str); | ||
510 | } | ||
511 | |||
512 | String8 String8::walkPath(String8* outRemains) const | ||
513 | { | ||
514 | const char* cp; | ||
515 | const char*const str = mString; | ||
516 | const char* buf = str; | ||
517 | |||
518 | cp = strchr(buf, OS_PATH_SEPARATOR); | ||
519 | if (cp == buf) { | ||
520 | // don't include a leading '/'. | ||
521 | buf = buf+1; | ||
522 | cp = strchr(buf, OS_PATH_SEPARATOR); | ||
523 | } | ||
524 | |||
525 | if (cp == NULL) { | ||
526 | String8 res = buf != str ? String8(buf) : *this; | ||
527 | if (outRemains) *outRemains = String8(""); | ||
528 | return res; | ||
529 | } | ||
530 | |||
531 | String8 res(buf, cp-buf); | ||
532 | if (outRemains) *outRemains = String8(cp+1); | ||
533 | return res; | ||
534 | } | ||
535 | |||
536 | /* | ||
537 | * Helper function for finding the start of an extension in a pathname. | ||
538 | * | ||
539 | * Returns a pointer inside mString, or NULL if no extension was found. | ||
540 | */ | ||
541 | char* String8::find_extension(void) const | ||
542 | { | ||
543 | const char* lastSlash; | ||
544 | const char* lastDot; | ||
545 | int extLen; | ||
546 | const char* const str = mString; | ||
547 | |||
548 | // only look at the filename | ||
549 | lastSlash = strrchr(str, OS_PATH_SEPARATOR); | ||
550 | if (lastSlash == NULL) | ||
551 | lastSlash = str; | ||
552 | else | ||
553 | lastSlash++; | ||
554 | |||
555 | // find the last dot | ||
556 | lastDot = strrchr(lastSlash, '.'); | ||
557 | if (lastDot == NULL) | ||
558 | return NULL; | ||
559 | |||
560 | // looks good, ship it | ||
561 | return const_cast<char*>(lastDot); | ||
562 | } | ||
563 | |||
564 | String8 String8::getPathExtension(void) const | ||
565 | { | ||
566 | char* ext; | ||
567 | |||
568 | ext = find_extension(); | ||
569 | if (ext != NULL) | ||
570 | return String8(ext); | ||
571 | else | ||
572 | return String8(""); | ||
573 | } | ||
574 | |||
575 | String8 String8::getBasePath(void) const | ||
576 | { | ||
577 | char* ext; | ||
578 | const char* const str = mString; | ||
579 | |||
580 | ext = find_extension(); | ||
581 | if (ext == NULL) | ||
582 | return String8(*this); | ||
583 | else | ||
584 | return String8(str, ext - str); | ||
585 | } | ||
586 | |||
587 | String8& String8::appendPath(const char* name) | ||
588 | { | ||
589 | // TODO: The test below will fail for Win32 paths. Fix later or ignore. | ||
590 | if (name[0] != OS_PATH_SEPARATOR) { | ||
591 | if (*name == '\0') { | ||
592 | // nothing to do | ||
593 | return *this; | ||
594 | } | ||
595 | |||
596 | size_t len = length(); | ||
597 | if (len == 0) { | ||
598 | // no existing filename, just use the new one | ||
599 | setPathName(name); | ||
600 | return *this; | ||
601 | } | ||
602 | |||
603 | // make room for oldPath + '/' + newPath | ||
604 | int newlen = strlen(name); | ||
605 | |||
606 | char* buf = lockBuffer(len+1+newlen); | ||
607 | |||
608 | // insert a '/' if needed | ||
609 | if (buf[len-1] != OS_PATH_SEPARATOR) | ||
610 | buf[len++] = OS_PATH_SEPARATOR; | ||
611 | |||
612 | memcpy(buf+len, name, newlen+1); | ||
613 | len += newlen; | ||
614 | |||
615 | unlockBuffer(len); | ||
616 | |||
617 | return *this; | ||
618 | } else { | ||
619 | setPathName(name); | ||
620 | return *this; | ||
621 | } | ||
622 | } | ||
623 | |||
624 | String8& String8::convertToResPath() | ||
625 | { | ||
626 | #if OS_PATH_SEPARATOR != RES_PATH_SEPARATOR | ||
627 | size_t len = length(); | ||
628 | if (len > 0) { | ||
629 | char * buf = lockBuffer(len); | ||
630 | for (char * end = buf + len; buf < end; ++buf) { | ||
631 | if (*buf == OS_PATH_SEPARATOR) | ||
632 | *buf = RES_PATH_SEPARATOR; | ||
633 | } | ||
634 | unlockBuffer(len); | ||
635 | } | ||
636 | #endif | ||
637 | return *this; | ||
638 | } | ||
639 | |||
640 | }; // namespace android | ||