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