Tue, 04 Oct 2022 19:25:07 +0200
fix over-optimization of strstr
1. it's actually less performant to frequently read bytes
from an array instead of using the native word length
2. the SBO buffer should be local and not static to allow
multi-threading usage
1 /*
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3 *
4 * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
29 #include "cx/string.h"
30 #include "util_allocator.h"
32 #include <gtest/gtest.h>
34 #define EXPECT_ZERO_TERMINATED(str) EXPECT_EQ((str).ptr[(str).length], '\0')
36 TEST(String, construct) {
37 cxstring s1 = cx_str("1234");
38 cxstring s2 = cx_strn("abcd", 2);
39 cxmutstr s3 = cx_mutstr((char *) "1234");
40 cxmutstr s4 = cx_mutstrn((char *) "abcd", 2);
42 EXPECT_EQ(s1.length, 4);
43 EXPECT_EQ(s2.length, 2);
44 EXPECT_EQ(s3.length, 4);
45 EXPECT_EQ(s4.length, 2);
46 }
48 TEST(String, strfree) {
49 CxTestingAllocator alloc;
50 auto test = (char *) cxMalloc(&alloc, 16);
51 cxmutstr str = cx_mutstrn(test, 16);
52 ASSERT_EQ(str.ptr, test);
53 EXPECT_EQ(str.length, 16);
54 cx_strfree_a(&alloc, &str);
55 EXPECT_EQ(str.ptr, nullptr);
56 EXPECT_EQ(str.length, 0);
57 EXPECT_TRUE(alloc.verify());
58 }
60 TEST(String, strdup) {
61 cxstring str = CX_STR("test");
62 cxmutstr dup = cx_strdup(str);
63 ASSERT_EQ(dup.length, str.length);
64 EXPECT_STREQ(dup.ptr, str.ptr);
65 EXPECT_ZERO_TERMINATED(dup);
66 cx_strfree(&dup);
68 str.length = 2;
69 dup = cx_strdup(str);
70 ASSERT_EQ(dup.length, str.length);
71 EXPECT_STREQ(dup.ptr, "te");
72 EXPECT_ZERO_TERMINATED(dup);
73 cx_strfree(&dup);
74 }
76 TEST(String, strlen) {
77 cxstring s1 = CX_STR("1234");
78 cxstring s2 = CX_STR(".:.:.");
79 cxstring s3 = CX_STR("X");
81 size_t len0 = cx_strlen(0);
82 size_t len1 = cx_strlen(1, s1);
83 size_t len2 = cx_strlen(2, s1, s2);
84 size_t len3 = cx_strlen(3, s1, s2, s3);
86 EXPECT_EQ(len0, 0);
87 EXPECT_EQ(len1, 4);
88 EXPECT_EQ(len2, 9);
89 EXPECT_EQ(len3, 10);
90 }
92 TEST(String, strsubs) {
93 cxstring str = CX_STR("A test string");
95 cxstring sub = cx_strsubs(str, 0);
96 EXPECT_EQ(cx_strcmp(sub, str), 0);
98 sub = cx_strsubs(str, 2);
99 EXPECT_EQ(cx_strcmp(sub, cx_str("test string")), 0);
101 sub = cx_strsubs(str, 7);
102 EXPECT_EQ(cx_strcmp(sub, cx_str("string")), 0);
104 sub = cx_strsubs(str, 15);
105 EXPECT_EQ(cx_strcmp(sub, cx_str("")), 0);
107 sub = cx_strsubsl(str, 2, 4);
108 EXPECT_EQ(cx_strcmp(sub, cx_str("test")), 0);
110 sub = cx_strsubsl(str, 7, 3);
111 EXPECT_EQ(cx_strcmp(sub, cx_str("str")), 0);
113 sub = cx_strsubsl(str, 7, 20);
114 EXPECT_EQ(cx_strcmp(sub, cx_str("string")), 0);
116 // just for coverage, call the _m variant
117 auto m = cx_strsubs_m(cx_mutstrn(nullptr, 0), 0);
118 EXPECT_EQ(cx_strcmp(cx_strcast(m), cx_str("")), 0);
119 }
121 TEST(String, strchr) {
122 cxstring str = CX_STR("I will find you - and I will kill you");
124 cxstring notfound = cx_strchr(str, 'x');
125 EXPECT_EQ(notfound.length, 0);
127 cxstring result = cx_strchr(str, 'w');
128 EXPECT_EQ(result.length, 35);
129 EXPECT_STREQ(result.ptr, "will find you - and I will kill you");
131 // just for coverage, call the _m variant
132 auto m = cx_strchr_m(cx_mutstrn(nullptr, 0), 'a');
133 EXPECT_EQ(cx_strcmp(cx_strcast(m), cx_str("")), 0);
134 }
136 TEST(String, strrchr) {
137 cxstring str = CX_STR("I will find you - and I will kill you");
139 cxstring notfound = cx_strrchr(str, 'x');
140 EXPECT_EQ(notfound.length, 0);
142 cxstring result = cx_strrchr(str, 'w');
143 EXPECT_EQ(result.length, 13);
144 EXPECT_STREQ(result.ptr, "will kill you");
146 // just for coverage, call the _m variant
147 auto m = cx_strrchr_m(cx_mutstrn(nullptr, 0), 'a');
148 EXPECT_EQ(cx_strcmp(cx_strcast(m), cx_str("")), 0);
149 }
151 TEST(String, strstr) {
152 cxstring str = CX_STR("find the match in this string");
153 cxstring longstr = CX_STR(
154 "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijkl"
155 "mnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwx"
156 "yzabcdeababababnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghij"
157 "klmnopqrstuvwxyzaababababababababrstuvwxyzabcdefghijklmnopqrstuv"
158 "abababababababababababababababababababababababababababababababab"
159 "abababababababababababababababababababababababababababababababab"
160 "abababababababababababababababababababababababababababababababab"
161 "abababababababababababababababababababababababababababababababab"
162 "abababababababababababababababababababababababababababababababab"
163 "abababababababababababababababababababababababababababababababab"
164 "wxyz1234567890");
165 cxstring longstrpattern = CX_STR(
166 "abababababababababababababababababababababababababababababababab"
167 "abababababababababababababababababababababababababababababababab"
168 "abababababababababababababababababababababababababababababababab"
169 "abababababababababababababababababababababababababababababababab"
170 "abababababababababababababababababababababababababababababababab"
171 );
172 cxstring longstrresult = CX_STR(
173 "abababababababababababababababababababababababababababababababab"
174 "abababababababababababababababababababababababababababababababab"
175 "abababababababababababababababababababababababababababababababab"
176 "abababababababababababababababababababababababababababababababab"
177 "abababababababababababababababababababababababababababababababab"
178 "abababababababababababababababababababababababababababababababab"
179 "wxyz1234567890"
180 );
182 cxstring notfound = cx_strstr(str, cx_str("no match"));
183 EXPECT_EQ(notfound.length, 0);
185 cxstring result = cx_strstr(str, cx_str("match"));
186 EXPECT_EQ(result.length, 20);
187 EXPECT_STREQ(result.ptr, "match in this string");
189 result = cx_strstr(str, cx_str(""));
190 EXPECT_EQ(result.length, str.length);
191 EXPECT_STREQ(result.ptr, str.ptr);
193 result = cx_strstr(longstr, longstrpattern);
194 EXPECT_EQ(result.length, longstrresult.length);
195 EXPECT_STREQ(result.ptr, longstrresult.ptr);
197 // just for coverage, call the _m variant
198 auto mstr = cx_strdup(longstr);
199 auto m = cx_strstr_m(mstr, longstrpattern);
200 EXPECT_EQ(m.length, longstrresult.length);
201 EXPECT_STREQ(m.ptr, longstrresult.ptr);
202 cx_strfree(&mstr);
203 }
205 TEST(String, strcmp) {
206 cxstring str = CX_STR("compare this");
208 EXPECT_EQ(cx_strcmp(cx_str(""), cx_str("")), 0);
209 EXPECT_GT(cx_strcmp(str, cx_str("")), 0);
210 EXPECT_EQ(cx_strcmp(str, cx_str("compare this")), 0);
211 EXPECT_NE(cx_strcmp(str, cx_str("Compare This")), 0);
212 EXPECT_LT(cx_strcmp(str, cx_str("compare tool")), 0);
213 EXPECT_GT(cx_strcmp(str, cx_str("compare shit")), 0);
214 EXPECT_LT(cx_strcmp(str, cx_str("compare this not")), 0);
215 EXPECT_GT(cx_strcmp(str, cx_str("compare")), 0);
216 }
218 TEST(String, strcasecmp) {
219 cxstring str = CX_STR("compare this");
221 EXPECT_EQ(cx_strcasecmp(cx_str(""), cx_str("")), 0);
222 EXPECT_GT(cx_strcasecmp(str, cx_str("")), 0);
223 EXPECT_EQ(cx_strcasecmp(str, cx_str("compare this")), 0);
224 EXPECT_EQ(cx_strcasecmp(str, cx_str("Compare This")), 0);
225 EXPECT_LT(cx_strcasecmp(str, cx_str("compare tool")), 0);
226 EXPECT_GT(cx_strcasecmp(str, cx_str("compare shit")), 0);
227 EXPECT_LT(cx_strcasecmp(str, cx_str("compare this not")), 0);
228 EXPECT_GT(cx_strcasecmp(str, cx_str("compare")), 0);
229 }
231 TEST(String, strcat) {
232 cxstring s1 = CX_STR("12");
233 cxstring s2 = CX_STR("34");
234 cxstring s3 = CX_STR("56");
235 cxstring sn = {nullptr, 0};
237 CxTestingAllocator alloc;
239 cxmutstr t1 = cx_strcat_a(&alloc, 2, s1, s2);
240 EXPECT_EQ(cx_strcmp(cx_strcast(t1), cx_str("1234")), 0);
241 EXPECT_ZERO_TERMINATED(t1);
242 cx_strfree_a(&alloc, &t1);
244 cxmutstr t2 = cx_strcat_a(&alloc, 3, s1, s2, s3);
245 EXPECT_EQ(cx_strcmp(cx_strcast(t2), cx_str("123456")), 0);
246 EXPECT_ZERO_TERMINATED(t2);
247 cx_strfree_a(&alloc, &t2);
249 cxmutstr t3 = cx_strcat_a(&alloc, 6, s1, sn, s2, sn, s3, sn);
250 EXPECT_EQ(cx_strcmp(cx_strcast(t3), cx_str("123456")), 0);
251 EXPECT_ZERO_TERMINATED(t3);
252 cx_strfree_a(&alloc, &t3);
254 cxmutstr t4 = cx_strcat_a(&alloc, 2, sn, sn);
255 EXPECT_EQ(cx_strcmp(cx_strcast(t4), cx_str("")), 0);
256 EXPECT_ZERO_TERMINATED(t4);
257 cx_strfree_a(&alloc, &t4);
259 EXPECT_TRUE(alloc.verify());
260 }
262 TEST(String, strsplit) {
264 cxstring test = cx_str("this,is,a,csv,string");
265 size_t capa = 8;
266 cxstring list[8];
267 size_t n;
269 /* special case: empty string */
270 n = cx_strsplit(test, cx_str(""), capa, list);
271 ASSERT_EQ(n, 1);
272 EXPECT_EQ(cx_strcmp(list[0], test), 0);
274 /* no delimiter occurrence */
275 n = cx_strsplit(test, cx_str("z"), capa, list);
276 ASSERT_EQ(n, 1);
277 EXPECT_EQ(cx_strcmp(list[0], test), 0);
279 /* partially matching delimiter */
280 n = cx_strsplit(test, cx_str("is,not"), capa, list);
281 ASSERT_EQ(n, 1);
282 EXPECT_EQ(cx_strcmp(list[0], test), 0);
284 /* matching single-char delimiter */
285 n = cx_strsplit(test, cx_str(","), capa, list);
286 ASSERT_EQ(n, 5);
287 EXPECT_EQ(cx_strcmp(list[0], cx_str("this")), 0);
288 EXPECT_EQ(cx_strcmp(list[1], cx_str("is")), 0);
289 EXPECT_EQ(cx_strcmp(list[2], cx_str("a")), 0);
290 EXPECT_EQ(cx_strcmp(list[3], cx_str("csv")), 0);
291 EXPECT_EQ(cx_strcmp(list[4], cx_str("string")), 0);
293 /* matching multi-char delimiter */
294 n = cx_strsplit(test, cx_str("is"), capa, list);
295 ASSERT_EQ(n, 3);
296 EXPECT_EQ(cx_strcmp(list[0], cx_str("th")), 0);
297 EXPECT_EQ(cx_strcmp(list[1], cx_str(",")), 0);
298 EXPECT_EQ(cx_strcmp(list[2], cx_str(",a,csv,string")), 0);
300 /* bounded list using single-char delimiter */
301 n = cx_strsplit(test, cx_str(","), 3, list);
302 ASSERT_EQ(n, 3);
303 EXPECT_EQ(cx_strcmp(list[0], cx_str("this")), 0);
304 EXPECT_EQ(cx_strcmp(list[1], cx_str("is")), 0);
305 EXPECT_EQ(cx_strcmp(list[2], cx_str("a,csv,string")), 0);
307 /* bounded list using multi-char delimiter */
308 n = cx_strsplit(test, cx_str("is"), 2, list);
309 ASSERT_EQ(n, 2);
310 EXPECT_EQ(cx_strcmp(list[0], cx_str("th")), 0);
311 EXPECT_EQ(cx_strcmp(list[1], cx_str(",is,a,csv,string")), 0);
313 /* start with delimiter */
314 n = cx_strsplit(test, cx_str("this"), capa, list);
315 ASSERT_EQ(n, 2);
316 EXPECT_EQ(cx_strcmp(list[0], cx_str("")), 0);
317 EXPECT_EQ(cx_strcmp(list[1], cx_str(",is,a,csv,string")), 0);
319 /* end with delimiter */
320 n = cx_strsplit(test, cx_str("string"), capa, list);
321 ASSERT_EQ(n, 2);
322 EXPECT_EQ(cx_strcmp(list[0], cx_str("this,is,a,csv,")), 0);
323 EXPECT_EQ(cx_strcmp(list[1], cx_str("")), 0);
326 /* end with delimiter exceed bound */
327 n = cx_strsplit(cx_str("a,b,c,"), cx_str(","), 3, list);
328 ASSERT_EQ(n, 3);
329 EXPECT_EQ(cx_strcmp(list[0], cx_str("a")), 0);
330 EXPECT_EQ(cx_strcmp(list[1], cx_str("b")), 0);
331 EXPECT_EQ(cx_strcmp(list[2], cx_str("c,")), 0);
333 /* exact match */
334 n = cx_strsplit(test, cx_str("this,is,a,csv,string"), capa, list);
335 ASSERT_EQ(n, 2);
336 EXPECT_EQ(cx_strcmp(list[0], cx_str("")), 0);
337 EXPECT_EQ(cx_strcmp(list[1], cx_str("")), 0);
339 /* string to be split is only substring */
340 n = cx_strsplit(test, cx_str("this,is,a,csv,string,with,extension"), capa, list);
341 ASSERT_EQ(n, 1);
342 EXPECT_EQ(cx_strcmp(list[0], test), 0);
344 /* subsequent encounter of delimiter (the string between is empty) */
345 n = cx_strsplit(test, cx_str("is,"), capa, list);
346 ASSERT_EQ(n, 3);
347 EXPECT_EQ(cx_strcmp(list[0], cx_str("th")), 0);
348 EXPECT_EQ(cx_strcmp(list[1], cx_str("")), 0);
349 EXPECT_EQ(cx_strcmp(list[2], cx_str("a,csv,string")), 0);
351 /* call the _m variant just for coverage */
352 auto mtest = cx_strdup(test);
353 cxmutstr mlist[4];
354 n = cx_strsplit_m(mtest, cx_str("is,"), 4, mlist);
355 ASSERT_EQ(n, 3);
356 EXPECT_EQ(cx_strcmp(cx_strcast(mlist[0]), cx_str("th")), 0);
357 EXPECT_EQ(cx_strcmp(cx_strcast(mlist[1]), cx_str("")), 0);
358 EXPECT_EQ(cx_strcmp(cx_strcast(mlist[2]), cx_str("a,csv,string")), 0);
359 cx_strfree(&mtest);
360 }
362 TEST(String, strsplit_a) {
363 CxTestingAllocator alloc;
365 cxstring test = cx_str("this,is,a,csv,string");
366 size_t capa = 8;
367 cxstring *list;
368 size_t n;
370 /* special case: empty string */
371 n = cx_strsplit_a(&alloc, test, cx_str(""), capa, &list);
372 ASSERT_EQ(n, 1);
373 EXPECT_EQ(cx_strcmp(list[0], test), 0);
374 cxFree(&alloc, list);
376 /* no delimiter occurrence */
377 n = cx_strsplit_a(&alloc, test, cx_str("z"), capa, &list);
378 ASSERT_EQ(n, 1);
379 EXPECT_EQ(cx_strcmp(list[0], test), 0);
380 cxFree(&alloc, list);
382 /* partially matching delimiter */
383 n = cx_strsplit_a(&alloc, test, cx_str("is,not"), capa, &list);
384 ASSERT_EQ(n, 1);
385 EXPECT_EQ(cx_strcmp(list[0], test), 0);
386 cxFree(&alloc, list);
388 /* matching single-char delimiter */
389 n = cx_strsplit_a(&alloc, test, cx_str(","), capa, &list);
390 ASSERT_EQ(n, 5);
391 EXPECT_EQ(cx_strcmp(list[0], cx_str("this")), 0);
392 EXPECT_EQ(cx_strcmp(list[1], cx_str("is")), 0);
393 EXPECT_EQ(cx_strcmp(list[2], cx_str("a")), 0);
394 EXPECT_EQ(cx_strcmp(list[3], cx_str("csv")), 0);
395 EXPECT_EQ(cx_strcmp(list[4], cx_str("string")), 0);
396 cxFree(&alloc, list);
398 /* matching multi-char delimiter */
399 n = cx_strsplit_a(&alloc, test, cx_str("is"), capa, &list);
400 ASSERT_EQ(n, 3);
401 EXPECT_EQ(cx_strcmp(list[0], cx_str("th")), 0);
402 EXPECT_EQ(cx_strcmp(list[1], cx_str(",")), 0);
403 EXPECT_EQ(cx_strcmp(list[2], cx_str(",a,csv,string")), 0);
404 cxFree(&alloc, list);
406 /* bounded list using single-char delimiter */
407 n = cx_strsplit_a(&alloc, test, cx_str(","), 3, &list);
408 ASSERT_EQ(n, 3);
409 EXPECT_EQ(cx_strcmp(list[0], cx_str("this")), 0);
410 EXPECT_EQ(cx_strcmp(list[1], cx_str("is")), 0);
411 EXPECT_EQ(cx_strcmp(list[2], cx_str("a,csv,string")), 0);
412 cxFree(&alloc, list);
414 /* bounded list using multi-char delimiter */
415 n = cx_strsplit_a(&alloc, test, cx_str("is"), 2, &list);
416 ASSERT_EQ(n, 2);
417 EXPECT_EQ(cx_strcmp(list[0], cx_str("th")), 0);
418 EXPECT_EQ(cx_strcmp(list[1], cx_str(",is,a,csv,string")), 0);
419 cxFree(&alloc, list);
421 /* start with delimiter */
422 n = cx_strsplit_a(&alloc, test, cx_str("this"), capa, &list);
423 ASSERT_EQ(n, 2);
424 EXPECT_EQ(cx_strcmp(list[0], cx_str("")), 0);
425 EXPECT_EQ(cx_strcmp(list[1], cx_str(",is,a,csv,string")), 0);
426 cxFree(&alloc, list);
428 /* end with delimiter */
429 n = cx_strsplit_a(&alloc, test, cx_str("string"), capa, &list);
430 ASSERT_EQ(n, 2);
431 EXPECT_EQ(cx_strcmp(list[0], cx_str("this,is,a,csv,")), 0);
432 EXPECT_EQ(cx_strcmp(list[1], cx_str("")), 0);
433 cxFree(&alloc, list);
435 /* end with delimiter exceed bound */
436 n = cx_strsplit_a(&alloc, cx_str("a,b,c,"), cx_str(","), 3, &list);
437 ASSERT_EQ(n, 3);
438 EXPECT_EQ(cx_strcmp(list[0], cx_str("a")), 0);
439 EXPECT_EQ(cx_strcmp(list[1], cx_str("b")), 0);
440 EXPECT_EQ(cx_strcmp(list[2], cx_str("c,")), 0);
441 cxFree(&alloc, list);
443 /* exact match */
444 n = cx_strsplit_a(&alloc, test, cx_str("this,is,a,csv,string"), capa, &list);
445 ASSERT_EQ(n, 2);
446 EXPECT_EQ(cx_strcmp(list[0], cx_str("")), 0);
447 EXPECT_EQ(cx_strcmp(list[1], cx_str("")), 0);
448 cxFree(&alloc, list);
450 /* string to be split is only substring */
451 n = cx_strsplit_a(&alloc, test, cx_str("this,is,a,csv,string,with,extension"), capa, &list);
452 ASSERT_EQ(n, 1);
453 EXPECT_EQ(cx_strcmp(list[0], test), 0);
454 cxFree(&alloc, list);
456 /* subsequent encounter of delimiter (the string between is empty) */
457 n = cx_strsplit_a(&alloc, test, cx_str("is,"), capa, &list);
458 ASSERT_EQ(n, 3);
459 EXPECT_EQ(cx_strcmp(list[0], cx_str("th")), 0);
460 EXPECT_EQ(cx_strcmp(list[1], cx_str("")), 0);
461 EXPECT_EQ(cx_strcmp(list[2], cx_str("a,csv,string")), 0);
462 cxFree(&alloc, list);
464 /* call the _m variant just for coverage */
465 auto mtest = cx_strdup(test);
466 cxmutstr *mlist;
467 n = cx_strsplit_ma(&alloc, mtest, cx_str("is,"), 4, &mlist);
468 ASSERT_EQ(n, 3);
469 EXPECT_EQ(cx_strcmp(cx_strcast(mlist[0]), cx_str("th")), 0);
470 EXPECT_EQ(cx_strcmp(cx_strcast(mlist[1]), cx_str("")), 0);
471 EXPECT_EQ(cx_strcmp(cx_strcast(mlist[2]), cx_str("a,csv,string")), 0);
472 cxFree(&alloc, mlist);
473 cx_strfree(&mtest);
475 EXPECT_TRUE(alloc.verify());
476 }
478 TEST(String, strtrim) {
479 cxstring t1 = cx_strtrim(cx_str(" ein test \t "));
480 cxstring t2 = cx_strtrim(cx_str("abc"));
481 cxstring t3 = cx_strtrim(cx_str(" 123"));
482 cxstring t4 = cx_strtrim(cx_str("xyz "));
483 cxstring t5 = cx_strtrim(cx_str(" "));
484 cxstring empty = cx_strtrim(cx_str(""));
486 EXPECT_EQ(cx_strcmp(t1, cx_str("ein test")), 0);
487 EXPECT_EQ(cx_strcmp(t2, cx_str("abc")), 0);
488 EXPECT_EQ(cx_strcmp(t3, cx_str("123")), 0);
489 EXPECT_EQ(cx_strcmp(t4, cx_str("xyz")), 0);
490 EXPECT_EQ(cx_strcmp(t5, cx_str("")), 0);
491 EXPECT_EQ(cx_strcmp(empty, cx_str("")), 0);
493 /* call the _m variant just for coverage */
494 cxmutstr m1 = cx_strtrim_m(cx_mutstr((char *) " ein test \t "));
495 EXPECT_EQ(cx_strcmp(cx_strcast(m1), cx_str("ein test")), 0);
496 }
498 TEST(String, strprefix) {
499 cxstring str = CX_STR("test my prefix and my suffix");
500 cxstring empty = CX_STR("");
501 EXPECT_FALSE(cx_strprefix(empty, cx_str("pref")));
502 EXPECT_TRUE(cx_strprefix(str, empty));
503 EXPECT_TRUE(cx_strprefix(empty, empty));
504 EXPECT_TRUE(cx_strprefix(str, cx_str("test ")));
505 EXPECT_FALSE(cx_strprefix(str, cx_str("8-) fsck ")));
506 }
508 TEST(String, strsuffix) {
509 cxstring str = CX_STR("test my prefix and my suffix");
510 cxstring empty = CX_STR("");
511 EXPECT_FALSE(cx_strsuffix(empty, cx_str("suf")));
512 EXPECT_TRUE(cx_strsuffix(str, empty));
513 EXPECT_TRUE(cx_strsuffix(empty, empty));
514 EXPECT_TRUE(cx_strsuffix(str, cx_str("fix")));
515 EXPECT_FALSE(cx_strsuffix(str, cx_str("fox")));
516 }
518 TEST(String, strcaseprefix) {
519 cxstring str = CX_STR("test my prefix and my suffix");
520 cxstring empty = CX_STR("");
521 EXPECT_FALSE(cx_strcaseprefix(empty, cx_str("pREf")));
522 EXPECT_TRUE(cx_strcaseprefix(str, empty));
523 EXPECT_TRUE(cx_strcaseprefix(empty, empty));
524 EXPECT_TRUE(cx_strcaseprefix(str, cx_str("TEST ")));
525 EXPECT_FALSE(cx_strcaseprefix(str, cx_str("8-) fsck ")));
526 }
528 TEST(String, strcasesuffix) {
529 cxstring str = CX_STR("test my prefix and my suffix");
530 cxstring empty = CX_STR("");
531 EXPECT_FALSE(cx_strcasesuffix(empty, cx_str("sUf")));
532 EXPECT_TRUE(cx_strcasesuffix(str, empty));
533 EXPECT_TRUE(cx_strcasesuffix(empty, empty));
534 EXPECT_TRUE(cx_strcasesuffix(str, cx_str("FIX")));
535 EXPECT_FALSE(cx_strcasesuffix(str, cx_str("fox")));
536 }
538 TEST(String, strreplace) {
539 cxstring str = CX_STR("test ababab string aba");
540 cxstring longstr = CX_STR(
541 "xyaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacd");
542 cxstring notrail = CX_STR("test abab");
543 cxstring empty = CX_STR("");
544 cxstring astr = CX_STR("aaaaaaaaaa");
545 cxstring csstr = CX_STR("test AB ab TEST xyz");
547 cxmutstr repl = cx_strreplace(str, cx_str("abab"), cx_str("muchlonger"));
548 auto expected = "test muchlongerab string aba";
550 cxmutstr repln = cx_strreplacen(str, cx_str("ab"), cx_str("c"), 2);
551 auto expectedn = "test ccab string aba";
553 cxmutstr longrepl = cx_strreplace(longstr, cx_str("a"), cx_str("z"));
554 auto longexpect = "xyzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzcd";
556 cxmutstr replnotrail = cx_strreplace(notrail, cx_str("ab"), cx_str("z"));
557 auto notrailexpect = "test zz";
559 cxmutstr repleq = cx_strreplace(str, str, cx_str("hello"));
560 auto eqexpect = "hello";
562 cxmutstr replempty1 = cx_strreplace(empty, cx_str("ab"), cx_str("c")); // expect: empty
563 cxmutstr replempty2 = cx_strreplace(str, cx_str("abab"), empty);
564 auto emptyexpect2 = "test ab string aba";
566 cxmutstr replpre = cx_strreplace(str, cx_str("test "), cx_str("TEST "));
567 auto preexpected = "TEST ababab string aba";
569 cxmutstr replan1 = cx_strreplacen(astr, cx_str("a"), cx_str("x"), 1);
570 auto an1expected = "xaaaaaaaaa";
572 cxmutstr replan4 = cx_strreplacen(astr, cx_str("a"), cx_str("x"), 4);
573 auto an4expected = "xxxxaaaaaa";
575 cxmutstr replan9 = cx_strreplacen(astr, cx_str("a"), cx_str("x"), 9);
576 auto an9expected = "xxxxxxxxxa";
578 cxmutstr replan10 = cx_strreplacen(astr, cx_str("a"), cx_str("x"), 10);
579 auto an10expected = "xxxxxxxxxx";
581 cxmutstr replcs1 = cx_strreplace(csstr, cx_str("AB"), cx_str("*"));
582 auto cs1expected = "test * ab TEST xyz";
584 cxmutstr replcs2 = cx_strreplace(csstr, cx_str("test"), cx_str("TEST"));
585 auto cs2expected = "TEST AB ab TEST xyz";
588 EXPECT_NE(repl.ptr, str.ptr);
589 EXPECT_ZERO_TERMINATED(repl);
590 EXPECT_STREQ(repl.ptr, expected);
591 EXPECT_ZERO_TERMINATED(repln);
592 EXPECT_STREQ(repln.ptr, expectedn);
593 EXPECT_ZERO_TERMINATED(longrepl);
594 EXPECT_STREQ(longrepl.ptr, longexpect);
595 EXPECT_ZERO_TERMINATED(replnotrail);
596 EXPECT_STREQ(replnotrail.ptr, notrailexpect);
597 EXPECT_ZERO_TERMINATED(repleq);
598 EXPECT_STREQ(repleq.ptr, eqexpect);
599 EXPECT_ZERO_TERMINATED(replempty1);
600 EXPECT_STREQ(replempty1.ptr, "");
601 EXPECT_ZERO_TERMINATED(replempty2);
602 EXPECT_STREQ(replempty2.ptr, emptyexpect2);
603 EXPECT_ZERO_TERMINATED(replpre);
604 EXPECT_STREQ(replpre.ptr, preexpected);
605 EXPECT_ZERO_TERMINATED(replan1);
606 EXPECT_STREQ(replan1.ptr, an1expected);
607 EXPECT_ZERO_TERMINATED(replan4);
608 EXPECT_STREQ(replan4.ptr, an4expected);
609 EXPECT_ZERO_TERMINATED(replan9);
610 EXPECT_STREQ(replan9.ptr, an9expected);
611 EXPECT_ZERO_TERMINATED(replan10);
612 EXPECT_STREQ(replan10.ptr, an10expected);
613 EXPECT_ZERO_TERMINATED(replcs1);
614 EXPECT_STREQ(replcs1.ptr, cs1expected);
615 EXPECT_ZERO_TERMINATED(replcs2);
616 EXPECT_STREQ(replcs2.ptr, cs2expected);
618 cx_strfree(&repl);
619 cx_strfree(&repln);
620 cx_strfree(&longrepl);
621 cx_strfree(&replnotrail);
622 cx_strfree(&repleq);
623 cx_strfree(&replempty1);
624 cx_strfree(&replempty2);
625 cx_strfree(&replpre);
626 cx_strfree(&replan1);
627 cx_strfree(&replan4);
628 cx_strfree(&replan9);
629 cx_strfree(&replan10);
630 cx_strfree(&replcs1);
631 cx_strfree(&replcs2);
632 }
634 TEST(String, strupper) {
635 cxmutstr str = cx_strdup(cx_str("thIs 1s @ Te$t"));
636 cx_strupper(str);
637 EXPECT_STREQ(str.ptr, "THIS 1S @ TE$T");
638 cx_strfree(&str);
639 }
641 TEST(String, strlower) {
642 cxmutstr str = cx_strdup(cx_str("thIs 1s @ Te$t"));
643 cx_strlower(str);
644 EXPECT_STREQ(str.ptr, "this 1s @ te$t");
645 cx_strfree(&str);
646 }