]>
Commit | Line | Data |
---|---|---|
65ec6247 RD |
1 | // SciTE - Scintilla based Text Editor |
2 | /** @file SString.h | |
3 | ** A simple string class. | |
4 | **/ | |
5 | // Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org> | |
6 | // The License.txt file describes the conditions under which this software may be distributed. | |
7 | ||
8 | #ifndef SSTRING_H | |
9 | #define SSTRING_H | |
10 | ||
1a2fb4cd | 11 | // These functions are implemented because each platform calls them something different. |
65ec6247 | 12 | int CompareCaseInsensitive(const char *a, const char *b); |
a834585d | 13 | int CompareNCaseInsensitive(const char *a, const char *b, size_t len); |
65ec6247 RD |
14 | bool EqualCaseInsensitive(const char *a, const char *b); |
15 | ||
16 | // Define another string class. | |
17 | // While it would be 'better' to use std::string, that doubles the executable size. | |
18 | // An SString may contain embedded nul characters. | |
19 | ||
65ec6247 RD |
20 | /** |
21 | * @brief A simple string class. | |
1a2fb4cd | 22 | * |
65ec6247 RD |
23 | * Hold the length of the string for quick operations, |
24 | * can have a buffer bigger than the string to avoid too many memory allocations and copies. | |
a834585d RD |
25 | * May have embedded zeroes as a result of @a substitute, but relies too heavily on C string |
26 | * functions to allow reliable manipulations of these strings, other than simple appends, etc. | |
65ec6247 RD |
27 | **/ |
28 | class SString { | |
1a2fb4cd RD |
29 | public: |
30 | /** Type of string lengths (sizes) and positions (indexes). */ | |
a834585d | 31 | typedef size_t lenpos_t; |
1a2fb4cd RD |
32 | /** Out of bounds value indicating that the string argument should be measured. */ |
33 | enum { measure_length=0xffffffffU}; | |
34 | ||
35 | private: | |
36 | char *s; ///< The C string | |
37 | lenpos_t sSize; ///< The size of the buffer, less 1: ie. the maximum size of the string | |
38 | lenpos_t sLen; ///< The size of the string in s | |
39 | lenpos_t sizeGrowth; ///< Minimum growth size when appending strings | |
65ec6247 | 40 | enum { sizeGrowthDefault = 64 }; |
1a2fb4cd RD |
41 | bool grow(lenpos_t lenNew) { |
42 | while (sizeGrowth * 6 < lenNew) { | |
43 | sizeGrowth *= 2; | |
44 | } | |
45 | char *sNew = new char[lenNew + sizeGrowth + 1]; | |
46 | if (sNew) { | |
47 | if (s) { | |
48 | memcpy(sNew, s, sLen); | |
49 | delete []s; | |
50 | } | |
51 | s = sNew; | |
52 | s[sLen] = '\0'; | |
53 | sSize = lenNew + sizeGrowth; | |
54 | } | |
55 | return sNew != 0; | |
56 | } | |
65ec6247 | 57 | |
1a2fb4cd RD |
58 | SString &assign(const char *sOther, lenpos_t sSize_=measure_length) { |
59 | if (!sOther) { | |
60 | sSize_ = 0; | |
61 | } else if (sSize_ == measure_length) { | |
62 | sSize_ = strlen(sOther); | |
63 | } | |
64 | if (sSize > 0 && sSize_ <= sSize) { // Does not allocate new buffer if the current is big enough | |
65 | if (s && sSize_) { | |
a834585d | 66 | memcpy(s, sOther, sSize_); |
1a2fb4cd RD |
67 | } |
68 | s[sSize_] = '\0'; | |
69 | sLen = sSize_; | |
70 | } else { | |
71 | delete []s; | |
72 | s = StringAllocate(sOther, sSize_); | |
73 | if (s) { | |
74 | sSize = sSize_; // Allow buffer bigger than real string, thus providing space to grow | |
75 | sLen = strlen(s); | |
76 | } else { | |
77 | sSize = sLen = 0; | |
78 | } | |
79 | } | |
80 | return *this; | |
81 | } | |
65ec6247 | 82 | |
1a2fb4cd | 83 | public: |
65ec6247 RD |
84 | SString() : s(0), sSize(0), sLen(0), sizeGrowth(sizeGrowthDefault) { |
85 | } | |
86 | SString(const SString &source) : sizeGrowth(sizeGrowthDefault) { | |
1a2fb4cd | 87 | s = StringAllocate(source.s); |
65ec6247 RD |
88 | sSize = sLen = (s) ? strlen(s) : 0; |
89 | } | |
90 | SString(const char *s_) : sizeGrowth(sizeGrowthDefault) { | |
1a2fb4cd | 91 | s = StringAllocate(s_); |
65ec6247 RD |
92 | sSize = sLen = (s) ? strlen(s) : 0; |
93 | } | |
1a2fb4cd | 94 | SString(const char *s_, lenpos_t first, lenpos_t last) : sizeGrowth(sizeGrowthDefault) { |
a834585d | 95 | // note: expects the "last" argument to point one beyond the range end (a la STL iterators) |
1a2fb4cd | 96 | s = StringAllocate(s_ + first, last - first); |
65ec6247 RD |
97 | sSize = sLen = (s) ? strlen(s) : 0; |
98 | } | |
99 | SString(int i) : sizeGrowth(sizeGrowthDefault) { | |
100 | char number[32]; | |
101 | sprintf(number, "%0d", i); | |
1a2fb4cd RD |
102 | s = StringAllocate(number); |
103 | sSize = sLen = (s) ? strlen(s) : 0; | |
104 | } | |
105 | SString(double d, int precision) : sizeGrowth(sizeGrowthDefault) { | |
106 | char number[32]; | |
107 | sprintf(number, "%.*f", precision, d); | |
108 | s = StringAllocate(number); | |
65ec6247 RD |
109 | sSize = sLen = (s) ? strlen(s) : 0; |
110 | } | |
111 | ~SString() { | |
112 | delete []s; | |
113 | s = 0; | |
114 | sSize = 0; | |
115 | sLen = 0; | |
116 | } | |
1a2fb4cd | 117 | void clear() { |
65ec6247 RD |
118 | if (s) { |
119 | *s = '\0'; | |
120 | } | |
121 | sLen = 0; | |
122 | } | |
123 | /** Size of buffer. */ | |
1a2fb4cd | 124 | lenpos_t size() const { |
65ec6247 RD |
125 | if (s) |
126 | return sSize; | |
127 | else | |
128 | return 0; | |
129 | } | |
130 | /** Size of string in buffer. */ | |
1a2fb4cd | 131 | lenpos_t length() const { |
65ec6247 RD |
132 | return sLen; |
133 | } | |
65ec6247 RD |
134 | SString &operator=(const char *source) { |
135 | return assign(source); | |
136 | } | |
137 | SString &operator=(const SString &source) { | |
138 | if (this != &source) { | |
139 | assign(source.c_str()); | |
140 | } | |
141 | return *this; | |
142 | } | |
143 | bool operator==(const SString &sOther) const { | |
144 | if ((s == 0) && (sOther.s == 0)) | |
145 | return true; | |
146 | if ((s == 0) || (sOther.s == 0)) | |
147 | return false; | |
148 | return strcmp(s, sOther.s) == 0; | |
149 | } | |
150 | bool operator!=(const SString &sOther) const { | |
151 | return !operator==(sOther); | |
152 | } | |
153 | bool operator==(const char *sOther) const { | |
154 | if ((s == 0) && (sOther == 0)) | |
155 | return true; | |
156 | if ((s == 0) || (sOther == 0)) | |
157 | return false; | |
158 | return strcmp(s, sOther) == 0; | |
159 | } | |
160 | bool operator!=(const char *sOther) const { | |
161 | return !operator==(sOther); | |
162 | } | |
163 | bool contains(char ch) { | |
164 | if (s && *s) | |
165 | return strchr(s, ch) != 0; | |
166 | else | |
167 | return false; | |
168 | } | |
1a2fb4cd | 169 | void setsizegrowth(lenpos_t sizeGrowth_) { |
65ec6247 RD |
170 | sizeGrowth = sizeGrowth_; |
171 | } | |
172 | const char *c_str() const { | |
173 | if (s) | |
174 | return s; | |
175 | else | |
176 | return ""; | |
177 | } | |
178 | /** Give ownership of buffer to caller which must use delete[] to free buffer. */ | |
179 | char *detach() { | |
180 | char *sRet = s; | |
181 | s = 0; | |
182 | sSize = 0; | |
183 | sLen = 0; | |
184 | return sRet; | |
185 | } | |
1a2fb4cd | 186 | char operator[](lenpos_t i) const { |
65ec6247 RD |
187 | if (s && i < sSize) // Or < sLen? Depends on the use, both are OK |
188 | return s[i]; | |
189 | else | |
190 | return '\0'; | |
191 | } | |
a834585d RD |
192 | SString substr(lenpos_t subPos, lenpos_t subLen=measure_length) const { |
193 | if (subPos >= sLen) { | |
194 | return SString(); // return a null string if start index is out of bounds | |
195 | } | |
196 | if ((subLen == measure_length) || (subPos + subLen > sLen)) { | |
197 | subLen = sLen - subPos; // can't substr past end of source string | |
198 | } | |
199 | return SString(s, subPos, subPos + subLen); | |
200 | } | |
201 | SString &lowercase(lenpos_t subPos = 0, lenpos_t subLen=measure_length) { | |
202 | if ((subLen == measure_length) || (subPos + subLen > sLen)) { | |
203 | subLen = sLen - subPos; // don't apply past end of string | |
204 | } | |
205 | for (lenpos_t i = subPos; i < subPos + subLen; i++) { | |
206 | if (s[i] < 'A' || s[i] > 'Z') | |
207 | continue; | |
208 | else | |
209 | s[i] = static_cast<char>(s[i] - 'A' + 'a'); | |
210 | } | |
211 | return *this; | |
212 | } | |
1a2fb4cd RD |
213 | SString &append(const char *sOther, lenpos_t sLenOther=measure_length, char sep = '\0') { |
214 | if (!sOther) { | |
215 | return *this; | |
216 | } | |
217 | if (sLenOther == measure_length) { | |
65ec6247 | 218 | sLenOther = strlen(sOther); |
1a2fb4cd | 219 | } |
65ec6247 | 220 | int lenSep = 0; |
1a2fb4cd | 221 | if (sLen && sep) { // Only add a separator if not empty |
65ec6247 | 222 | lenSep = 1; |
1a2fb4cd RD |
223 | } |
224 | lenpos_t lenNew = sLen + sLenOther + lenSep; | |
225 | // Conservative about growing the buffer: don't do it, unless really needed | |
226 | if ((lenNew + 1 < sSize) || (grow(lenNew))) { | |
65ec6247 RD |
227 | if (lenSep) { |
228 | s[sLen] = sep; | |
229 | sLen++; | |
230 | } | |
a834585d | 231 | memcpy(&s[sLen], sOther, sLenOther); |
65ec6247 | 232 | sLen += sLenOther; |
1a2fb4cd | 233 | s[sLen] = '\0'; |
65ec6247 RD |
234 | } |
235 | return *this; | |
236 | } | |
1a2fb4cd RD |
237 | SString &operator+=(const char *sOther) { |
238 | return append(sOther, static_cast<lenpos_t>(measure_length)); | |
65ec6247 | 239 | } |
1a2fb4cd | 240 | SString &operator+=(const SString &sOther) { |
9e730a78 | 241 | return append(sOther.s, sOther.sLen); |
65ec6247 | 242 | } |
1a2fb4cd | 243 | SString &operator+=(char ch) { |
65ec6247 RD |
244 | return append(&ch, 1); |
245 | } | |
1a2fb4cd | 246 | SString &appendwithseparator(const char *sOther, char sep) { |
65ec6247 RD |
247 | return append(sOther, strlen(sOther), sep); |
248 | } | |
1a2fb4cd RD |
249 | SString &insert(lenpos_t pos, const char *sOther, lenpos_t sLenOther=measure_length) { |
250 | if (!sOther) { | |
251 | return *this; | |
252 | } | |
253 | if (sLenOther == measure_length) { | |
254 | sLenOther = strlen(sOther); | |
255 | } | |
256 | lenpos_t lenNew = sLen + sLenOther; | |
257 | // Conservative about growing the buffer: don't do it, unless really needed | |
258 | if ((lenNew + 1 < sSize) || grow(lenNew)) { | |
259 | lenpos_t moveChars = sLen - pos + 1; | |
260 | for (lenpos_t i = moveChars; i > 0; i--) { | |
261 | s[pos + sLenOther + i - 1] = s[pos + i - 1]; | |
262 | } | |
263 | memcpy(s + pos, sOther, sLenOther); | |
264 | sLen = lenNew; | |
265 | } | |
266 | return *this; | |
267 | } | |
268 | /** Remove @a len characters from the @a pos position, included. | |
269 | * Characters at pos + len and beyond replace characters at pos. | |
270 | * If @a len is 0, or greater than the length of the string | |
271 | * starting at @a pos, the string is just truncated at @a pos. | |
272 | */ | |
273 | void remove(lenpos_t pos, lenpos_t len) { | |
274 | if (len < 1 || pos + len >= sLen) { | |
275 | s[pos] = '\0'; | |
276 | sLen = pos; | |
277 | } else { | |
278 | for (lenpos_t i = pos; i < sLen - len + 1; i++) { | |
279 | s[i] = s[i+len]; | |
280 | } | |
281 | sLen -= len; | |
282 | } | |
283 | } | |
a834585d RD |
284 | SString &change(lenpos_t pos, char ch) { |
285 | if (pos >= sLen) { // character changed must be in string bounds | |
286 | return *this; | |
287 | } | |
288 | *(s + pos) = ch; | |
289 | return *this; | |
290 | } | |
1a2fb4cd | 291 | /** Read an integral numeric value from the string. */ |
65ec6247 RD |
292 | int value() const { |
293 | if (s) | |
294 | return atoi(s); | |
295 | else | |
296 | return 0; | |
297 | } | |
a834585d | 298 | int search(const char *sFind, lenpos_t start=0) const { |
1a2fb4cd RD |
299 | if (start < sLen) { |
300 | const char *sFound = strstr(s + start, sFind); | |
301 | if (sFound) { | |
302 | return sFound - s; | |
303 | } | |
304 | } | |
305 | return -1; | |
306 | } | |
307 | bool contains(const char *sFind) { | |
308 | return search(sFind) >= 0; | |
309 | } | |
310 | int substitute(char chFind, char chReplace) { | |
311 | int c = 0; | |
65ec6247 RD |
312 | char *t = s; |
313 | while (t) { | |
1a2fb4cd | 314 | t = strchr(t, chFind); |
65ec6247 | 315 | if (t) { |
1a2fb4cd | 316 | *t = chReplace; |
65ec6247 | 317 | t++; |
1a2fb4cd | 318 | c++; |
65ec6247 RD |
319 | } |
320 | } | |
1a2fb4cd RD |
321 | return c; |
322 | } | |
323 | int substitute(const char *sFind, const char *sReplace) { | |
324 | int c = 0; | |
325 | lenpos_t lenFind = strlen(sFind); | |
326 | lenpos_t lenReplace = strlen(sReplace); | |
327 | int posFound = search(sFind); | |
328 | while (posFound >= 0) { | |
329 | remove(posFound, lenFind); | |
330 | insert(posFound, sReplace, lenReplace); | |
331 | posFound = search(sFind, posFound + lenReplace); | |
332 | c++; | |
333 | } | |
334 | return c; | |
335 | } | |
336 | int remove(const char *sFind) { | |
337 | return substitute(sFind, ""); | |
338 | } | |
339 | /** | |
340 | * Duplicate a C string. | |
341 | * Allocate memory of the given size, or big enough to fit the string if length isn't given; | |
342 | * then copy the given string in the allocated memory. | |
343 | * @return the pointer to the new string | |
344 | */ | |
345 | static char *StringAllocate( | |
346 | const char *s, ///< The string to duplicate | |
347 | lenpos_t len=measure_length) ///< The length of memory to allocate. Optional. | |
348 | { | |
349 | if (s == 0) { | |
350 | return 0; | |
351 | } | |
352 | if (len == measure_length) { | |
353 | len = strlen(s); | |
354 | } | |
355 | char *sNew = new char[len + 1]; | |
356 | if (sNew) { | |
a834585d | 357 | memcpy(sNew, s, len); |
1a2fb4cd RD |
358 | sNew[len] = '\0'; |
359 | } | |
360 | return sNew; | |
65ec6247 RD |
361 | } |
362 | }; | |
363 | ||
1a2fb4cd RD |
364 | /** |
365 | * Duplicate a C string. | |
366 | * Allocate memory of the given size, or big enough to fit the string if length isn't given; | |
367 | * then copy the given string in the allocated memory. | |
368 | * @return the pointer to the new string | |
369 | */ | |
370 | inline char *StringDup( | |
371 | const char *s, ///< The string to duplicate | |
9e730a78 | 372 | SString::lenpos_t len=SString::measure_length) ///< The length of memory to allocate. Optional. |
1a2fb4cd RD |
373 | { |
374 | return SString::StringAllocate(s, len); | |
375 | } | |
376 | ||
65ec6247 | 377 | #endif |