initial commit
[rofl0r-xstring.git] / xstring.c
blob4277e471ebaabba27f2e6ea9242330df0c5dd45e
1 /**
2 * XString (c) 2010 by rofl0r.
3 *
4 * licensed under GNU LGPL 2.1+
5 *
6 * use at your own risk.
7 */
10 #include <stdarg.h>
11 #include <ctype.h>
12 #include <stdio.h>
13 #include <string.h>
15 #define ASSERT_NOT_NULL
17 #ifdef ASSERT_NOT_NULL
18 #define USE_ASSERT
19 #endif
21 #ifdef USE_ASSERT
22 #include <assert.h>
23 #endif
25 #include "xstring.h"
27 static const uchar _XSTR_WHITESPACE[4] = " \r\n\t";
28 static const xint _XSTR_WHITESPACE_SIZE = sizeof(_XSTR_WHITESPACE);
30 static inline xint xstr_rshift(xstr* str) {
31 return (str->mallocaddr == NULL || str->data == NULL ) ? 0 : (xint) str->data - (xint) str->mallocaddr;
34 static inline bool xstr_is_shortstring(xstr* str) {
35 return ((xuint) str->data == (xuint) &str->data + sizeof(void*));
38 /* use like this
39 * xshortstr512 temp; // let it be allocated on the stack
40 * xstr* myStr = xshortstr_init((void*)&temp, 512); // work with myStr from now on.
42 xstr* xshortstr_init(void* str, xuint bufsize) {
43 // it doesnt matter which shortstring type we cast to here, since mem layout is the same.
44 xshortstr512* it = (xshortstr512*) str;
45 it->data = &it->buffer[0];
46 it->mallocaddr = it->data;
47 it->size = 0;
48 it->capacity = bufsize - 1;
49 return (xstr*) str;
52 /* create a new string from a literal. if size passed is 0, strlen will be called instead */
53 xstr* xstr_fromliteral(const char* literal, xuint size) {
54 assert(literal != NULL);
55 if(size==0) size = strlen((char*) literal);
56 xstr* self = xstr_create();
57 self->data = (uchar*) literal;
58 self->size = size;
59 return self;
62 xstr* xstr_create(void) {
63 return (xstr*) calloc(1,sizeof(xstr));
66 xstr* xstr_new(xuint size) {
67 xstr* self = xstr_create();
68 return xstr_init(self, size);
71 /* free's a xstr - call only on heap alloced ones */
72 void xstr_free(xstr* self) {
73 #ifdef ASSERT_NOT_NULL
74 assert(self != NULL);
75 #endif
76 if(self->mallocaddr && (void*) self->mallocaddr != (void*) self)
77 free(self->mallocaddr);
78 free(self);
81 /* shifts data pointer to the right count bytes if possible
82 if count is bigger as possible shifts right maximum possible
83 size and capacity is decreased accordingly */
84 // remark: can be called with negative value (done by leftShift)
85 void xstr_shiftright (xstr* self, xint count) {
86 #ifdef ASSERT_NOT_NULL
87 assert(self != NULL);
88 #endif
89 if (count == 0 || self->size == 0) return;
90 xint c = count;
91 xuint rshift = xstr_rshift(self);
92 if (c > (xint) self->size) c = self->size;
93 else if (c < 0 && abs(c) > rshift) c = rshift *-1;
94 self->data += c;
95 self->size -= c;
98 /* shifts back count bytes, only possible if shifted right before */
99 void xstr_shiftleft(xstr* self, xint count) {
100 if (count) xstr_shiftright (self, -count); // it can be so easy
104 * Adjust this buffer's capacity, reallocate memory if needed.
106 * @param newCapacity The number of bytes (not characters) this buffer
107 * should be capable of storing
108 * returns false if no more memory could be alloc'd
110 bool xstr_setcapacity(xstr* self, xuint newcapacity) {
111 #ifdef ASSERT_NOT_NULL
112 assert(self != NULL);
113 #endif
114 xuint _rshift = xstr_rshift(self);
115 xuint min = newcapacity + 1 + _rshift;
117 if(min >= self->capacity) {
118 if (xstr_is_shortstring(self)) return false;
119 // allocate 20% + 10 bytes more than needed - just in case
120 self->capacity = (min * 120) / 100 + 10;
122 // align at 8 byte boundary for performance
123 xuint al = 8 - (self->capacity % 8);
124 if (al < 8) self->capacity += al;
126 xuint rs = _rshift;
127 if (rs) xstr_shiftleft(self, rs);
129 unsigned char* tmp = (unsigned char*) realloc(self->mallocaddr, self->capacity);
130 if (!tmp) return false;
132 // if we were coming from a string literal, copy the original data as well (gc_realloc cant work on text segment)
133 if(self->size > 0 && self->mallocaddr == NULL) {
134 rs = 0;
135 memcpy(tmp, self->data, self->size);
138 self->mallocaddr = tmp;
139 self->data = tmp;
140 if (rs) xstr_shiftright(self, rs);
142 // just to be sure to be always zero terminated
143 self->data[newcapacity] = '\0';
144 return true;
147 xstr* xstr_init(xstr* self, xuint capacity) {
148 #ifdef ASSERT_NOT_NULL
149 assert(self != NULL);
150 #endif
151 self->capacity = 0;
152 self->data = NULL;
153 self->mallocaddr = NULL;
154 self->size = 0;
155 if(capacity) xstr_setcapacity(self, capacity);
156 return self;
159 // sets capacity and size flag, and a zero termination */
160 bool xstr_setlength(xstr* self, xuint new_length) {
161 #ifdef ASSERT_NOT_NULL
162 assert(self != NULL);
163 #endif
164 bool ret = true;
165 if(new_length != self->size) {
166 if(new_length > self->capacity) {
167 ret = xstr_setcapacity(self, new_length);
169 if (ret) {
170 self->size = new_length;
171 self->data[self->size] = '\0';
174 return ret;
177 // @return true if the string is empty */
178 bool xstr_empty(xstr* self){
179 #ifdef ASSERT_NOT_NULL
180 assert(self != NULL);
181 #endif
182 return self->size == 0;
185 // compare *length* characters of *this* with *other*, starting at *start*.
186 // Return true if the two strings are equal, return false if they are not. */
187 bool xstr_compare (xstr* self, xstr* other, xint start, xint length) {
188 #ifdef ASSERT_NOT_NULL
189 assert(self != NULL);
190 #endif
191 //"comparing %s and %s, start = %zd, length = %zd, size = %zd, start + length = %zd" printfln(data, other data, start, length, size, start + length)
192 if (self->size < (start + length) || other->size < length) return false;
194 xint i;
195 for(i = 0; i < length; ++i) {
196 if(self->data[start + i] != other->data[i]) {
197 return false;
200 return true;
203 // return true if *other* and *this* are equal (in terms of being null / having same size and content). */
204 bool xstr_equals (xstr* self, xstr* other) {
205 //"equaling %s and %s, size = %zd, other size = %zd" printfln(data, other data, size, other size)
206 if ((self == NULL) && (other != NULL)) return false;
207 if ((other == NULL) && (self != NULL)) return false;
208 if ((other == NULL) && (self == NULL)) return true;
209 if(other->size != self->size) return false;
210 return !memcmp(self->data, other->data, self->size);
213 // @return true if the first characters of *this* are equal to *s*. */
214 bool xstr_startswith (xstr* self, xstr* s) {
215 #ifdef ASSERT_NOT_NULL
216 assert(self != NULL);
217 #endif
218 if (self->size < s->size) return false;
219 return xstr_compare(self, s, 0, s->size);
222 // @return true if the first character of *this* is equal to *c*. */
223 bool xstr_startswith_char (xstr* self, uchar c) {
224 #ifdef ASSERT_NOT_NULL
225 assert(self != NULL);
226 #endif
227 return (self->size > 0) && (self->data[0] == c);
230 // @return true if the last characters of *this* are equal to *s*. */
231 bool xstr_endswith (xstr* self, xstr* s) {
232 #ifdef ASSERT_NOT_NULL
233 assert(self != NULL);
234 #endif
235 if (self->size < s->size) return false;
236 return xstr_compare(self, s, self->size - s->size, s->size);
239 // @return true if the last character of *this* is equal to *c*. */
240 bool xstr_endswith_char(xstr* self, uchar c) {
241 #ifdef ASSERT_NOT_NULL
242 assert(self != NULL);
243 #endif
244 return (self->size > 0) && self->data[self->size] == c;
247 bool xstr_contains_pointer(xstr* self, uchar* what, xuint whatsize) {
248 return(xstr_find_pointer(self, what, whatsize, 0, true) != -1);
251 bool xstr_contains_char(xstr* self, uchar what) {
252 return(xstr_contains_pointer(self, &what, 1));
255 bool xstr_contains_xstr(xstr* self, xstr* what) {
256 #ifdef ASSERT_NOT_NULL
257 assert(self != NULL);
258 #endif
259 return(xstr_contains_pointer(self, what->data, what->size));
262 // return the number of *what*'s occurences in *this*. */
263 xuint xstr_count_char(xstr* self, uchar what, bool searchCaseSensitive) {
264 return xstr_count_pointer(self, &what, 1, searchCaseSensitive);
267 // return the number of *what*'s non-overlapping occurences in *this*. */
268 xuint xstr_count_xstr(xstr* self, xstr* what, bool searchCaseSensitive){
269 #ifdef ASSERT_NOT_NULL
270 assert(self != NULL);
271 #endif
272 return xstr_count_pointer(self, what->data, what->size, searchCaseSensitive);
275 // return the number of *what*'s non-overlapping occurences in *this*. */
276 xuint xstr_count_pointer(xstr* self, uchar* what, xuint whatSize, bool searchCaseSensitive){
277 xuint result = 0;
278 xint offset = (whatSize) * -1;
279 while (((offset = xstr_find_pointer(self, what, whatSize, offset + whatSize, searchCaseSensitive)) != -1)) result++;
280 return result;
284 returns -1 when not found, otherwise the position of the first occurence of "what"
285 use offset 0 for a new search, then increase it by the last found position +1
286 look at implementation of findAll() for an example
288 xint xstr_find (xstr* self, xstr* what, xint offset, bool searchCaseSensitive) {
289 return xstr_find_pointer(self, what->data, what->size, offset, searchCaseSensitive);
292 xint xstr_find_char(xstr* self, uchar what, xint offset, bool searchCaseSensitive) {
293 return xstr_find_pointer (self, &what, 1, offset, searchCaseSensitive);
296 xint xstr_find_pointer (xstr* self, uchar* what, xuint whatSize, xint offset, bool searchCaseSensitive) {
297 #ifdef ASSERT_NOT_NULL
298 assert(self != NULL);
299 #endif
300 if (offset >= self->size || offset < 0 || what == NULL || whatSize == 0) return -1;
302 xint maxpos = self->size - whatSize; // need a signed type here
303 if (maxpos < 0) return -1;
305 bool found;
306 xint sstart = offset;
308 for (sstart = offset; sstart < (maxpos + 1); ++sstart) {
309 found = true;
310 xint j;
311 for (j = 0; j < whatSize; ++j) {
312 if (searchCaseSensitive) {
313 if (self->data[sstart + j] != what[j] ) {
314 found = false;
315 break;
317 } else {
318 if (toupper(self->data[sstart + j]) != toupper(what[j])) {
319 found = false;
320 break;
324 if (found) return sstart;
326 return -1;
329 // Converts all of the characters in this Buffer to lower case.
330 void xstr_tolower(xstr* self) {
331 #ifdef ASSERT_NOT_NULL
332 assert(self != NULL);
333 #endif
334 xint i;
335 for(i = 0; i < self->size; ++i) {
336 self->data[i] = tolower(self->data[i]);
340 //Converts all of the characters in this Buffer to lower case.
341 void xstr_toupper(xstr* self) {
342 #ifdef ASSERT_NOT_NULL
343 assert(self != NULL);
344 #endif
345 xint i;
346 for(i=0; i < self->size; ++i) {
347 self->data[i] = toupper(self->data[i]);
351 void xstr_trim_pointer(xstr* self, uchar* s, xuint sLength) {
352 xstr_trimright_pointer(self, s, sLength);
353 xstr_trimleft_pointer(self, s, sLength);
356 void xstr_trim_xstr(xstr* self, xstr* s) {
357 xstr_trim_pointer(self, s->data, s->size);
360 // *c* characters stripped at both ends. */
361 void xstr_trim_char(xstr* self, uchar c) {
362 xstr_trim_pointer(self, &c, 1);
365 // whitespace characters (space, CR, LF, tab) stripped at both ends. */
366 void xstr_trim_whitespace(xstr* self) {
367 xstr_trim_pointer(self, (uchar*) _XSTR_WHITESPACE, _XSTR_WHITESPACE_SIZE);
370 void xstr_trim_space(xstr* self) {
371 xstr_trim_char(self, ' ');
374 // *c* character stripped from the left side. */
375 void xstr_trimleft_char(xstr* self, uchar c) {
376 xstr_trimleft_pointer(self, &c, 1);
379 // all characters _contained in_ *s* stripped from the left side. either from *this* or a clone */
380 void xstr_trimleft_xstr(xstr* self, xstr* s) {
381 xstr_trimleft_pointer(self, s->data, s->size);
384 // all characters _contained in_ *s* stripped from the left side. either from *this* or a clone */
385 void xstr_trimleft_pointer(xstr* self, uchar* s, xuint sLength) {
386 #ifdef ASSERT_NOT_NULL
387 assert(self != NULL);
388 #endif
389 if (self->size == 0 || sLength == 0) return;
390 xuint start = 0;
391 while (start < self->size && xchar_containedin_pointer(*(self->data + start), s, sLength)) start += 1;
392 if(start == 0) return;
393 xstr_shiftright(self, start);
396 // whitespace characters (space, CR, LF, tab) stripped at left end. */
397 void xstr_trimleft_whitespace(xstr* self) {
398 xstr_trimleft_pointer(self, (uchar*) _XSTR_WHITESPACE, _XSTR_WHITESPACE_SIZE);
401 void xstr_trimleft_space(xstr* self) {
402 xstr_trimleft_char(self, ' ');
405 // *c* characters stripped from the right side. */
406 void xstr_trimright_char(xstr* self, uchar c) {
407 xstr_trimright_pointer(self, &c, 1);
410 // *this* with all characters contained by *s* stripped from the right side.
411 void xstr_trimright_xstr(xstr* self, xstr* s) {
412 xstr_trimright_pointer(self, s->data, s->size);
415 // @return (a copy of) *this* with all characters contained by *s* stripped from the right side
416 void xstr_trimright_pointer(xstr* self, uchar* s, xuint sLength) {
417 #ifdef ASSERT_NOT_NULL
418 assert(self != NULL);
419 #endif
420 xint end = self->size;
421 while(end > 0 && xchar_containedin_pointer(self->data[end - 1], s, sLength))
422 end--;
423 if (end != self->size)
424 xstr_setlength(self, end);
427 // whitespace characters (space, CR, LF, tab) stripped at right end. */
428 void xstr_trimright_whitespace(xstr* self) {
429 xstr_trimright_pointer(self, (uchar*) _XSTR_WHITESPACE, _XSTR_WHITESPACE_SIZE);
432 // space characters (ASCII 32) stripped from the right side. */
433 void xstr_trimright_space(xstr* self) {
434 xstr_trimright_char(self, ' ');
438 // reverses self. "ABBA" -> "ABBA" .no. joke. "ABC" -> "CBA" */
439 void xstr_reverse(xstr* self) {
440 #ifdef ASSERT_NOT_NULL
441 assert(self != NULL);
442 #endif
443 xuint bytesLeft = self->size;
444 xint i = 0;
445 while (bytesLeft > 1) {
446 uchar c = self->data[i];
447 self->data[i] = self->data[self->size - 1 - i];
448 self->data[self->size - 1 - i] = c;
449 bytesLeft -= 2;
450 i += 1;
454 // use like this: xstr_append(str1, str2, str3, NULL);
455 void xstr_append(xstr* self, ...) {
456 #ifdef ASSERT_NOT_NULL
457 assert(self != NULL);
458 #endif
459 xuint totalsize = self->size;
460 xstr* candidate = NULL;
461 xint save;
463 va_list ap;
464 va_start(ap, self);
465 while(1) {
466 candidate = va_arg(ap, xstr*);
467 if(candidate == NULL) break;
468 totalsize += candidate->size;
470 va_end(ap);
471 save = self->size;
472 if (xstr_setlength(self, totalsize)) {
473 uchar* dest = self->data + save;
474 va_start(ap, self);
475 while(1) {
476 candidate = va_arg(ap, xstr*);
477 if(candidate == NULL) break;
478 memcpy(dest, candidate->data, candidate->size);
479 dest += candidate->size;
481 va_end(ap);
485 int xstr_puts(xstr* self) {
486 #ifdef ASSERT_NOT_NULL
487 assert(self != NULL);
488 #endif
489 return puts((char*) self->data);