epoger@google.com | ec3ed6a | 2011-07-28 14:26:00 +0000 | [diff] [blame] | 1 | |
reed@android.com | 8a1c16f | 2008-12-17 15:59:43 +0000 | [diff] [blame] | 2 | /* |
epoger@google.com | ec3ed6a | 2011-07-28 14:26:00 +0000 | [diff] [blame] | 3 | * Copyright 2006 The Android Open Source Project |
reed@android.com | 8a1c16f | 2008-12-17 15:59:43 +0000 | [diff] [blame] | 4 | * |
epoger@google.com | ec3ed6a | 2011-07-28 14:26:00 +0000 | [diff] [blame] | 5 | * Use of this source code is governed by a BSD-style license that can be |
| 6 | * found in the LICENSE file. |
reed@android.com | 8a1c16f | 2008-12-17 15:59:43 +0000 | [diff] [blame] | 7 | */ |
| 8 | |
epoger@google.com | ec3ed6a | 2011-07-28 14:26:00 +0000 | [diff] [blame] | 9 | |
reed@android.com | 8a1c16f | 2008-12-17 15:59:43 +0000 | [diff] [blame] | 10 | #ifndef SkString_DEFINED |
| 11 | #define SkString_DEFINED |
| 12 | |
| 13 | #include "SkScalar.h" |
rmistry@google.com | d6bab02 | 2013-12-02 13:50:38 +0000 | [diff] [blame] | 14 | #include "SkTArray.h" |
reed@android.com | 8a1c16f | 2008-12-17 15:59:43 +0000 | [diff] [blame] | 15 | |
bsalomon@google.com | b9cf393 | 2013-03-07 18:05:08 +0000 | [diff] [blame] | 16 | #include <stdarg.h> |
| 17 | |
reed@android.com | 8a1c16f | 2008-12-17 15:59:43 +0000 | [diff] [blame] | 18 | /* Some helper functions for C strings |
| 19 | */ |
| 20 | |
epoger@google.com | e8ebeb1 | 2012-10-29 16:42:11 +0000 | [diff] [blame] | 21 | static bool SkStrStartsWith(const char string[], const char prefixStr[]) { |
epoger@google.com | c4ae974 | 2012-04-27 17:11:31 +0000 | [diff] [blame] | 22 | SkASSERT(string); |
epoger@google.com | e8ebeb1 | 2012-10-29 16:42:11 +0000 | [diff] [blame] | 23 | SkASSERT(prefixStr); |
| 24 | return !strncmp(string, prefixStr, strlen(prefixStr)); |
epoger@google.com | c4ae974 | 2012-04-27 17:11:31 +0000 | [diff] [blame] | 25 | } |
epoger@google.com | e8ebeb1 | 2012-10-29 16:42:11 +0000 | [diff] [blame] | 26 | static bool SkStrStartsWith(const char string[], const char prefixChar) { |
| 27 | SkASSERT(string); |
| 28 | return (prefixChar == *string); |
| 29 | } |
| 30 | |
| 31 | bool SkStrEndsWith(const char string[], const char suffixStr[]); |
| 32 | bool SkStrEndsWith(const char string[], const char suffixChar); |
| 33 | |
reed@android.com | 8a1c16f | 2008-12-17 15:59:43 +0000 | [diff] [blame] | 34 | int SkStrStartsWithOneOf(const char string[], const char prefixes[]); |
epoger@google.com | e8ebeb1 | 2012-10-29 16:42:11 +0000 | [diff] [blame] | 35 | |
humper@google.com | 7af56be | 2013-01-14 18:49:19 +0000 | [diff] [blame] | 36 | static int SkStrFind(const char string[], const char substring[]) { |
humper@google.com | 61a972f | 2013-01-14 19:03:46 +0000 | [diff] [blame] | 37 | const char *first = strstr(string, substring); |
humper@google.com | 7af56be | 2013-01-14 18:49:19 +0000 | [diff] [blame] | 38 | if (NULL == first) return -1; |
reed@google.com | 3d5b998 | 2013-02-04 22:40:28 +0000 | [diff] [blame] | 39 | return SkToS32(first - &string[0]); |
humper@google.com | 7af56be | 2013-01-14 18:49:19 +0000 | [diff] [blame] | 40 | } |
| 41 | |
epoger@google.com | c4ae974 | 2012-04-27 17:11:31 +0000 | [diff] [blame] | 42 | static bool SkStrContains(const char string[], const char substring[]) { |
| 43 | SkASSERT(string); |
| 44 | SkASSERT(substring); |
humper@google.com | 7af56be | 2013-01-14 18:49:19 +0000 | [diff] [blame] | 45 | return (-1 != SkStrFind(string, substring)); |
epoger@google.com | c4ae974 | 2012-04-27 17:11:31 +0000 | [diff] [blame] | 46 | } |
epoger@google.com | e8ebeb1 | 2012-10-29 16:42:11 +0000 | [diff] [blame] | 47 | static bool SkStrContains(const char string[], const char subchar) { |
| 48 | SkASSERT(string); |
humper@google.com | 7af56be | 2013-01-14 18:49:19 +0000 | [diff] [blame] | 49 | char tmp[2]; |
| 50 | tmp[0] = subchar; |
| 51 | tmp[1] = '\0'; |
| 52 | return (-1 != SkStrFind(string, tmp)); |
epoger@google.com | e8ebeb1 | 2012-10-29 16:42:11 +0000 | [diff] [blame] | 53 | } |
reed@android.com | 8a1c16f | 2008-12-17 15:59:43 +0000 | [diff] [blame] | 54 | |
humper@google.com | 7af56be | 2013-01-14 18:49:19 +0000 | [diff] [blame] | 55 | static inline char *SkStrDup(const char string[]) { |
| 56 | char *ret = (char *) sk_malloc_throw(strlen(string)+1); |
robertphillips@google.com | 37ebe3f | 2013-03-04 20:03:44 +0000 | [diff] [blame] | 57 | memcpy(ret,string,strlen(string)+1); |
humper@google.com | 7af56be | 2013-01-14 18:49:19 +0000 | [diff] [blame] | 58 | return ret; |
| 59 | } |
| 60 | |
| 61 | |
| 62 | |
epoger@google.com | d88a3d8 | 2013-06-19 18:27:20 +0000 | [diff] [blame] | 63 | #define SkStrAppendU32_MaxSize 10 |
epoger@google.com | 27d3025 | 2013-06-19 18:52:42 +0000 | [diff] [blame] | 64 | char* SkStrAppendU32(char buffer[], uint32_t); |
epoger@google.com | d88a3d8 | 2013-06-19 18:27:20 +0000 | [diff] [blame] | 65 | #define SkStrAppendU64_MaxSize 20 |
epoger@google.com | 27d3025 | 2013-06-19 18:52:42 +0000 | [diff] [blame] | 66 | char* SkStrAppendU64(char buffer[], uint64_t, int minDigits); |
epoger@google.com | d88a3d8 | 2013-06-19 18:27:20 +0000 | [diff] [blame] | 67 | |
| 68 | #define SkStrAppendS32_MaxSize (SkStrAppendU32_MaxSize + 1) |
reed@android.com | 8a1c16f | 2008-12-17 15:59:43 +0000 | [diff] [blame] | 69 | char* SkStrAppendS32(char buffer[], int32_t); |
epoger@google.com | d88a3d8 | 2013-06-19 18:27:20 +0000 | [diff] [blame] | 70 | #define SkStrAppendS64_MaxSize (SkStrAppendU64_MaxSize + 1) |
vandebo@chromium.org | d877fdb | 2010-10-12 23:08:13 +0000 | [diff] [blame] | 71 | char* SkStrAppendS64(char buffer[], int64_t, int minDigits); |
reed@google.com | 8072e4f | 2011-03-01 15:44:08 +0000 | [diff] [blame] | 72 | |
| 73 | /** |
| 74 | * Floats have at most 8 significant digits, so we limit our %g to that. |
reed@google.com | e280f1d | 2011-03-03 18:43:14 +0000 | [diff] [blame] | 75 | * However, the total string could be 15 characters: -1.2345678e-005 |
| 76 | * |
| 77 | * In theory we should only expect up to 2 digits for the exponent, but on |
| 78 | * some platforms we have seen 3 (as in the example above). |
reed@google.com | 8072e4f | 2011-03-01 15:44:08 +0000 | [diff] [blame] | 79 | */ |
reed@google.com | e280f1d | 2011-03-03 18:43:14 +0000 | [diff] [blame] | 80 | #define SkStrAppendScalar_MaxSize 15 |
reed@google.com | 8072e4f | 2011-03-01 15:44:08 +0000 | [diff] [blame] | 81 | |
| 82 | /** |
| 83 | * Write the scaler in decimal format into buffer, and return a pointer to |
| 84 | * the next char after the last one written. Note: a terminating 0 is not |
| 85 | * written into buffer, which must be at least SkStrAppendScalar_MaxSize. |
| 86 | * Thus if the caller wants to add a 0 at the end, buffer must be at least |
| 87 | * SkStrAppendScalar_MaxSize + 1 bytes large. |
| 88 | */ |
reed@google.com | 8f4d230 | 2013-12-17 16:44:46 +0000 | [diff] [blame^] | 89 | #define SkStrAppendScalar SkStrAppendFloat |
vandebo@chromium.org | 677cbed | 2011-03-03 18:20:12 +0000 | [diff] [blame] | 90 | |
vandebo@chromium.org | 677cbed | 2011-03-03 18:20:12 +0000 | [diff] [blame] | 91 | char* SkStrAppendFloat(char buffer[], float); |
vandebo@chromium.org | 677cbed | 2011-03-03 18:20:12 +0000 | [diff] [blame] | 92 | char* SkStrAppendFixed(char buffer[], SkFixed); |
reed@android.com | 8a1c16f | 2008-12-17 15:59:43 +0000 | [diff] [blame] | 93 | |
| 94 | /** \class SkString |
| 95 | |
| 96 | Light weight class for managing strings. Uses reference |
| 97 | counting to make string assignments and copies very fast |
| 98 | with no extra RAM cost. Assumes UTF8 encoding. |
| 99 | */ |
vandebo@chromium.org | a56fedc | 2012-10-18 23:26:44 +0000 | [diff] [blame] | 100 | class SK_API SkString { |
reed@android.com | 8a1c16f | 2008-12-17 15:59:43 +0000 | [diff] [blame] | 101 | public: |
| 102 | SkString(); |
| 103 | explicit SkString(size_t len); |
| 104 | explicit SkString(const char text[]); |
| 105 | SkString(const char text[], size_t len); |
reed@android.com | 8015dd8 | 2009-06-21 00:49:18 +0000 | [diff] [blame] | 106 | SkString(const SkString&); |
reed@android.com | 8a1c16f | 2008-12-17 15:59:43 +0000 | [diff] [blame] | 107 | ~SkString(); |
| 108 | |
reed@google.com | 4bce115 | 2011-09-14 16:13:58 +0000 | [diff] [blame] | 109 | bool isEmpty() const { return 0 == fRec->fLength; } |
reed@android.com | 8a1c16f | 2008-12-17 15:59:43 +0000 | [diff] [blame] | 110 | size_t size() const { return (size_t) fRec->fLength; } |
| 111 | const char* c_str() const { return fRec->data(); } |
reed@android.com | 8015dd8 | 2009-06-21 00:49:18 +0000 | [diff] [blame] | 112 | char operator[](size_t n) const { return this->c_str()[n]; } |
reed@android.com | 8a1c16f | 2008-12-17 15:59:43 +0000 | [diff] [blame] | 113 | |
mike@reedtribe.org | 4e1d3ac | 2011-04-10 01:04:37 +0000 | [diff] [blame] | 114 | bool equals(const SkString&) const; |
| 115 | bool equals(const char text[]) const; |
| 116 | bool equals(const char text[], size_t len) const; |
reed@android.com | 8a1c16f | 2008-12-17 15:59:43 +0000 | [diff] [blame] | 117 | |
epoger@google.com | e8ebeb1 | 2012-10-29 16:42:11 +0000 | [diff] [blame] | 118 | bool startsWith(const char prefixStr[]) const { |
| 119 | return SkStrStartsWith(fRec->data(), prefixStr); |
reed@android.com | 8a1c16f | 2008-12-17 15:59:43 +0000 | [diff] [blame] | 120 | } |
epoger@google.com | e8ebeb1 | 2012-10-29 16:42:11 +0000 | [diff] [blame] | 121 | bool startsWith(const char prefixChar) const { |
| 122 | return SkStrStartsWith(fRec->data(), prefixChar); |
| 123 | } |
| 124 | bool endsWith(const char suffixStr[]) const { |
| 125 | return SkStrEndsWith(fRec->data(), suffixStr); |
| 126 | } |
| 127 | bool endsWith(const char suffixChar) const { |
| 128 | return SkStrEndsWith(fRec->data(), suffixChar); |
reed@android.com | 8a1c16f | 2008-12-17 15:59:43 +0000 | [diff] [blame] | 129 | } |
epoger@google.com | c4ae974 | 2012-04-27 17:11:31 +0000 | [diff] [blame] | 130 | bool contains(const char substring[]) const { |
| 131 | return SkStrContains(fRec->data(), substring); |
| 132 | } |
epoger@google.com | e8ebeb1 | 2012-10-29 16:42:11 +0000 | [diff] [blame] | 133 | bool contains(const char subchar) const { |
| 134 | return SkStrContains(fRec->data(), subchar); |
| 135 | } |
humper@google.com | 7af56be | 2013-01-14 18:49:19 +0000 | [diff] [blame] | 136 | int find(const char substring[]) const { |
| 137 | return SkStrFind(fRec->data(), substring); |
| 138 | } |
reed@android.com | 8a1c16f | 2008-12-17 15:59:43 +0000 | [diff] [blame] | 139 | |
reed@google.com | b530ef5 | 2011-07-20 19:55:42 +0000 | [diff] [blame] | 140 | friend bool operator==(const SkString& a, const SkString& b) { |
reed@android.com | 8a1c16f | 2008-12-17 15:59:43 +0000 | [diff] [blame] | 141 | return a.equals(b); |
| 142 | } |
reed@google.com | b530ef5 | 2011-07-20 19:55:42 +0000 | [diff] [blame] | 143 | friend bool operator!=(const SkString& a, const SkString& b) { |
reed@android.com | 8a1c16f | 2008-12-17 15:59:43 +0000 | [diff] [blame] | 144 | return !a.equals(b); |
| 145 | } |
| 146 | |
| 147 | // these methods edit the string |
| 148 | |
reed@google.com | b530ef5 | 2011-07-20 19:55:42 +0000 | [diff] [blame] | 149 | SkString& operator=(const SkString&); |
| 150 | SkString& operator=(const char text[]); |
reed@android.com | 8a1c16f | 2008-12-17 15:59:43 +0000 | [diff] [blame] | 151 | |
reed@google.com | b530ef5 | 2011-07-20 19:55:42 +0000 | [diff] [blame] | 152 | char* writable_str(); |
reed@android.com | 8015dd8 | 2009-06-21 00:49:18 +0000 | [diff] [blame] | 153 | char& operator[](size_t n) { return this->writable_str()[n]; } |
reed@android.com | 8a1c16f | 2008-12-17 15:59:43 +0000 | [diff] [blame] | 154 | |
mike@reedtribe.org | 4e1d3ac | 2011-04-10 01:04:37 +0000 | [diff] [blame] | 155 | void reset(); |
| 156 | void resize(size_t len) { this->set(NULL, len); } |
| 157 | void set(const SkString& src) { *this = src; } |
| 158 | void set(const char text[]); |
| 159 | void set(const char text[], size_t len); |
| 160 | void setUTF16(const uint16_t[]); |
| 161 | void setUTF16(const uint16_t[], size_t len); |
reed@android.com | 8a1c16f | 2008-12-17 15:59:43 +0000 | [diff] [blame] | 162 | |
mike@reedtribe.org | 4e1d3ac | 2011-04-10 01:04:37 +0000 | [diff] [blame] | 163 | void insert(size_t offset, const SkString& src) { this->insert(offset, src.c_str(), src.size()); } |
| 164 | void insert(size_t offset, const char text[]); |
| 165 | void insert(size_t offset, const char text[], size_t len); |
| 166 | void insertUnichar(size_t offset, SkUnichar); |
| 167 | void insertS32(size_t offset, int32_t value); |
| 168 | void insertS64(size_t offset, int64_t value, int minDigits = 0); |
epoger@google.com | d88a3d8 | 2013-06-19 18:27:20 +0000 | [diff] [blame] | 169 | void insertU32(size_t offset, uint32_t value); |
| 170 | void insertU64(size_t offset, uint64_t value, int minDigits = 0); |
mike@reedtribe.org | 4e1d3ac | 2011-04-10 01:04:37 +0000 | [diff] [blame] | 171 | void insertHex(size_t offset, uint32_t value, int minDigits = 0); |
| 172 | void insertScalar(size_t offset, SkScalar); |
reed@android.com | 8a1c16f | 2008-12-17 15:59:43 +0000 | [diff] [blame] | 173 | |
mike@reedtribe.org | 4e1d3ac | 2011-04-10 01:04:37 +0000 | [diff] [blame] | 174 | void append(const SkString& str) { this->insert((size_t)-1, str); } |
| 175 | void append(const char text[]) { this->insert((size_t)-1, text); } |
| 176 | void append(const char text[], size_t len) { this->insert((size_t)-1, text, len); } |
| 177 | void appendUnichar(SkUnichar uni) { this->insertUnichar((size_t)-1, uni); } |
| 178 | void appendS32(int32_t value) { this->insertS32((size_t)-1, value); } |
| 179 | void appendS64(int64_t value, int minDigits = 0) { this->insertS64((size_t)-1, value, minDigits); } |
epoger@google.com | d88a3d8 | 2013-06-19 18:27:20 +0000 | [diff] [blame] | 180 | void appendU32(uint32_t value) { this->insertU32((size_t)-1, value); } |
| 181 | void appendU64(uint64_t value, int minDigits = 0) { this->insertU64((size_t)-1, value, minDigits); } |
mike@reedtribe.org | 4e1d3ac | 2011-04-10 01:04:37 +0000 | [diff] [blame] | 182 | void appendHex(uint32_t value, int minDigits = 0) { this->insertHex((size_t)-1, value, minDigits); } |
| 183 | void appendScalar(SkScalar value) { this->insertScalar((size_t)-1, value); } |
reed@android.com | 8a1c16f | 2008-12-17 15:59:43 +0000 | [diff] [blame] | 184 | |
mike@reedtribe.org | 4e1d3ac | 2011-04-10 01:04:37 +0000 | [diff] [blame] | 185 | void prepend(const SkString& str) { this->insert(0, str); } |
| 186 | void prepend(const char text[]) { this->insert(0, text); } |
| 187 | void prepend(const char text[], size_t len) { this->insert(0, text, len); } |
| 188 | void prependUnichar(SkUnichar uni) { this->insertUnichar(0, uni); } |
| 189 | void prependS32(int32_t value) { this->insertS32(0, value); } |
| 190 | void prependS64(int32_t value, int minDigits = 0) { this->insertS64(0, value, minDigits); } |
| 191 | void prependHex(uint32_t value, int minDigits = 0) { this->insertHex(0, value, minDigits); } |
| 192 | void prependScalar(SkScalar value) { this->insertScalar((size_t)-1, value); } |
reed@android.com | 8a1c16f | 2008-12-17 15:59:43 +0000 | [diff] [blame] | 193 | |
senorblanco@chromium.org | 3a67a66 | 2012-07-09 18:22:08 +0000 | [diff] [blame] | 194 | void printf(const char format[], ...) SK_PRINTF_LIKE(2, 3); |
| 195 | void appendf(const char format[], ...) SK_PRINTF_LIKE(2, 3); |
commit-bot@chromium.org | ce0e4ef | 2013-11-21 17:20:17 +0000 | [diff] [blame] | 196 | void appendVAList(const char format[], va_list); |
senorblanco@chromium.org | 3a67a66 | 2012-07-09 18:22:08 +0000 | [diff] [blame] | 197 | void prependf(const char format[], ...) SK_PRINTF_LIKE(2, 3); |
reed@android.com | 8a1c16f | 2008-12-17 15:59:43 +0000 | [diff] [blame] | 198 | |
mike@reedtribe.org | 4e1d3ac | 2011-04-10 01:04:37 +0000 | [diff] [blame] | 199 | void remove(size_t offset, size_t length); |
reed@android.com | 8a1c16f | 2008-12-17 15:59:43 +0000 | [diff] [blame] | 200 | |
bsalomon@google.com | fc29629 | 2011-05-06 13:53:47 +0000 | [diff] [blame] | 201 | SkString& operator+=(const SkString& s) { this->append(s); return *this; } |
| 202 | SkString& operator+=(const char text[]) { this->append(text); return *this; } |
epoger@google.com | 4a8084c | 2013-05-09 20:01:26 +0000 | [diff] [blame] | 203 | SkString& operator+=(const char c) { this->append(&c, 1); return *this; } |
bsalomon@google.com | fc29629 | 2011-05-06 13:53:47 +0000 | [diff] [blame] | 204 | |
mike@reedtribe.org | 4e1d3ac | 2011-04-10 01:04:37 +0000 | [diff] [blame] | 205 | /** |
| 206 | * Swap contents between this and other. This function is guaranteed |
| 207 | * to never fail or throw. |
| 208 | */ |
| 209 | void swap(SkString& other); |
reed@google.com | 8072e4f | 2011-03-01 15:44:08 +0000 | [diff] [blame] | 210 | |
reed@android.com | 8a1c16f | 2008-12-17 15:59:43 +0000 | [diff] [blame] | 211 | private: |
| 212 | struct Rec { |
| 213 | public: |
reed@google.com | 8cb1088 | 2013-06-04 20:36:52 +0000 | [diff] [blame] | 214 | uint32_t fLength; // logically size_t, but we want it to stay 32bits |
reed@google.com | 4bce115 | 2011-09-14 16:13:58 +0000 | [diff] [blame] | 215 | int32_t fRefCnt; |
reed@android.com | 8a1c16f | 2008-12-17 15:59:43 +0000 | [diff] [blame] | 216 | char fBeginningOfData; |
| 217 | |
| 218 | char* data() { return &fBeginningOfData; } |
| 219 | const char* data() const { return &fBeginningOfData; } |
| 220 | }; |
| 221 | Rec* fRec; |
| 222 | |
| 223 | #ifdef SK_DEBUG |
| 224 | const char* fStr; |
| 225 | void validate() const; |
| 226 | #else |
| 227 | void validate() const {} |
| 228 | #endif |
| 229 | |
| 230 | static const Rec gEmptyRec; |
reed@google.com | 4bce115 | 2011-09-14 16:13:58 +0000 | [diff] [blame] | 231 | static Rec* AllocRec(const char text[], size_t len); |
reed@android.com | 8a1c16f | 2008-12-17 15:59:43 +0000 | [diff] [blame] | 232 | static Rec* RefRec(Rec*); |
| 233 | }; |
| 234 | |
tomhudson@google.com | 3a1f6a0 | 2011-06-30 14:39:52 +0000 | [diff] [blame] | 235 | /// Creates a new string and writes into it using a printf()-style format. |
| 236 | SkString SkStringPrintf(const char* format, ...); |
| 237 | |
bsalomon@google.com | ff43661 | 2013-02-27 19:07:32 +0000 | [diff] [blame] | 238 | // Specialized to take advantage of SkString's fast swap path. The unspecialized function is |
| 239 | // declared in SkTypes.h and called by SkTSort. |
| 240 | template <> inline void SkTSwap(SkString& a, SkString& b) { |
| 241 | a.swap(b); |
| 242 | } |
| 243 | |
rmistry@google.com | d6bab02 | 2013-12-02 13:50:38 +0000 | [diff] [blame] | 244 | // Split str on any characters in delimiters into out. (Think, strtok with a sane API.) |
| 245 | void SkStrSplit(const char* str, const char* delimiters, SkTArray<SkString>* out); |
| 246 | |
reed@android.com | 8a1c16f | 2008-12-17 15:59:43 +0000 | [diff] [blame] | 247 | #endif |