2 * XString (c) 2010 by rofl0r.
4 * licensed under GNU LGPL 2.1+
6 * use at your own risk.
15 #define ASSERT_NOT_NULL
17 #ifdef ASSERT_NOT_NULL
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*));
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
;
48 it
->capacity
= bufsize
- 1;
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
;
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
76 if(self
->mallocaddr
&& (void*) self
->mallocaddr
!= (void*) self
)
77 free(self
->mallocaddr
);
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
89 if (count
== 0 || self
->size
== 0) return;
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;
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
);
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
;
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
) {
135 memcpy(tmp
, self
->data
, self
->size
);
138 self
->mallocaddr
= tmp
;
140 if (rs
) xstr_shiftright(self
, rs
);
142 // just to be sure to be always zero terminated
143 self
->data
[newcapacity
] = '\0';
147 xstr
* xstr_init(xstr
* self
, xuint capacity
) {
148 #ifdef ASSERT_NOT_NULL
149 assert(self
!= NULL
);
153 self
->mallocaddr
= NULL
;
155 if(capacity
) xstr_setcapacity(self
, capacity
);
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
);
165 if(new_length
!= self
->size
) {
166 if(new_length
> self
->capacity
) {
167 ret
= xstr_setcapacity(self
, new_length
);
170 self
->size
= new_length
;
171 self
->data
[self
->size
] = '\0';
177 // @return true if the string is empty */
178 bool xstr_empty(xstr
* self
){
179 #ifdef ASSERT_NOT_NULL
180 assert(self
!= NULL
);
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
);
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;
195 for(i
= 0; i
< length
; ++i
) {
196 if(self
->data
[start
+ i
] != other
->data
[i
]) {
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
);
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
);
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
);
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
);
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
);
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
);
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
){
278 xint offset
= (whatSize
) * -1;
279 while (((offset
= xstr_find_pointer(self
, what
, whatSize
, offset
+ whatSize
, searchCaseSensitive
)) != -1)) 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
);
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;
306 xint sstart
= offset
;
308 for (sstart
= offset
; sstart
< (maxpos
+ 1); ++sstart
) {
311 for (j
= 0; j
< whatSize
; ++j
) {
312 if (searchCaseSensitive
) {
313 if (self
->data
[sstart
+ j
] != what
[j
] ) {
318 if (toupper(self
->data
[sstart
+ j
]) != toupper(what
[j
])) {
324 if (found
) return sstart
;
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
);
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
);
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
);
389 if (self
->size
== 0 || sLength
== 0) return;
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
);
420 xint end
= self
->size
;
421 while(end
> 0 && xchar_containedin_pointer(self
->data
[end
- 1], s
, sLength
))
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
);
443 xuint bytesLeft
= self
->size
;
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
;
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
);
459 xuint totalsize
= self
->size
;
460 xstr
* candidate
= NULL
;
466 candidate
= va_arg(ap
, xstr
*);
467 if(candidate
== NULL
) break;
468 totalsize
+= candidate
->size
;
472 if (xstr_setlength(self
, totalsize
)) {
473 uchar
* dest
= self
->data
+ save
;
476 candidate
= va_arg(ap
, xstr
*);
477 if(candidate
== NULL
) break;
478 memcpy(dest
, candidate
->data
, candidate
->size
);
479 dest
+= candidate
->size
;
485 int xstr_puts(xstr
* self
) {
486 #ifdef ASSERT_NOT_NULL
487 assert(self
!= NULL
);
489 return puts((char*) self
->data
);