1 /***************************************************************************************************
2
3 Zyan Core Library (Zycore-C)
4
5 Original Author : Florian Bernd
6
7 * Permission is hereby granted, free of charge, to any person obtaining a copy
8 * of this software and associated documentation files (the "Software"), to deal
9 * in the Software without restriction, including without limitation the rights
10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 * copies of the Software, and to permit persons to whom the Software is
12 * furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included in all
15 * copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 * SOFTWARE.
24
25 ***************************************************************************************************/
26
27 #include <Zycore/String.h>
28 #include <Zycore/LibC.h>
29
30 /* ============================================================================================== */
31 /* Internal macros */
32 /* ============================================================================================== */
33
34 /**
35 * Writes a terminating '\0' character at the end of the string data.
36 */
37 #define ZYCORE_STRING_NULLTERMINATE(string) \
38 *(char*)((ZyanU8*)(string)->vector.data + (string)->vector.size - 1) = '\0';
39
40 /**
41 * Checks for a terminating '\0' character at the end of the string data.
42 */
43 #define ZYCORE_STRING_ASSERT_NULLTERMINATION(string) \
44 ZYAN_ASSERT(*(char*)((ZyanU8*)(string)->vector.data + (string)->vector.size - 1) == '\0');
45
46 /* ============================================================================================== */
47 /* Exported functions */
48 /* ============================================================================================== */
49
50 /* ---------------------------------------------------------------------------------------------- */
51 /* Constructor and destructor */
52 /* ---------------------------------------------------------------------------------------------- */
53
54 #ifndef ZYAN_NO_LIBC
55
ZyanStringInit(ZyanString * string,ZyanUSize capacity)56 ZyanStatus ZyanStringInit(ZyanString* string, ZyanUSize capacity)
57 {
58 return ZyanStringInitEx(string, capacity, ZyanAllocatorDefault(),
59 ZYAN_STRING_DEFAULT_GROWTH_FACTOR, ZYAN_STRING_DEFAULT_SHRINK_THRESHOLD);
60 }
61
62 #endif // ZYAN_NO_LIBC
63
ZyanStringInitEx(ZyanString * string,ZyanUSize capacity,ZyanAllocator * allocator,ZyanU8 growth_factor,ZyanU8 shrink_threshold)64 ZyanStatus ZyanStringInitEx(ZyanString* string, ZyanUSize capacity, ZyanAllocator* allocator,
65 ZyanU8 growth_factor, ZyanU8 shrink_threshold)
66 {
67 if (!string)
68 {
69 return ZYAN_STATUS_INVALID_ARGUMENT;
70 }
71
72 string->flags = 0;
73 capacity = ZYAN_MAX(ZYAN_STRING_MIN_CAPACITY, capacity) + 1;
74 ZYAN_CHECK(ZyanVectorInitEx(&string->vector, sizeof(char), capacity, ZYAN_NULL, allocator,
75 growth_factor, shrink_threshold));
76 ZYAN_ASSERT(string->vector.capacity >= capacity);
77 // Some of the string code relies on `sizeof(char) == 1`
78 ZYAN_ASSERT(string->vector.element_size == 1);
79
80 *(char*)string->vector.data = '\0';
81 ++string->vector.size;
82
83 return ZYAN_STATUS_SUCCESS;
84 }
85
ZyanStringInitCustomBuffer(ZyanString * string,char * buffer,ZyanUSize capacity)86 ZyanStatus ZyanStringInitCustomBuffer(ZyanString* string, char* buffer, ZyanUSize capacity)
87 {
88 if (!string || !capacity)
89 {
90 return ZYAN_STATUS_INVALID_ARGUMENT;
91 }
92
93 string->flags = ZYAN_STRING_HAS_FIXED_CAPACITY;
94 ZYAN_CHECK(ZyanVectorInitCustomBuffer(&string->vector, sizeof(char), (void*)buffer, capacity,
95 ZYAN_NULL));
96 ZYAN_ASSERT(string->vector.capacity == capacity);
97 // Some of the string code relies on `sizeof(char) == 1`
98 ZYAN_ASSERT(string->vector.element_size == 1);
99
100 *(char*)string->vector.data = '\0';
101 ++string->vector.size;
102
103 return ZYAN_STATUS_SUCCESS;
104 }
105
ZyanStringDestroy(ZyanString * string)106 ZyanStatus ZyanStringDestroy(ZyanString* string)
107 {
108 if (!string)
109 {
110 return ZYAN_STATUS_INVALID_ARGUMENT;
111 }
112 if (string->flags & ZYAN_STRING_HAS_FIXED_CAPACITY)
113 {
114 return ZYAN_STATUS_SUCCESS;
115 }
116
117 return ZyanVectorDestroy(&string->vector);
118 }
119
120 /* ---------------------------------------------------------------------------------------------- */
121 /* Duplication */
122 /* ---------------------------------------------------------------------------------------------- */
123
124 #ifndef ZYAN_NO_LIBC
125
ZyanStringDuplicate(ZyanString * destination,const ZyanStringView * source,ZyanUSize capacity)126 ZyanStatus ZyanStringDuplicate(ZyanString* destination, const ZyanStringView* source,
127 ZyanUSize capacity)
128 {
129 return ZyanStringDuplicateEx(destination, source, capacity, ZyanAllocatorDefault(),
130 ZYAN_STRING_DEFAULT_GROWTH_FACTOR, ZYAN_STRING_DEFAULT_SHRINK_THRESHOLD);
131 }
132
133 #endif // ZYAN_NO_LIBC
134
ZyanStringDuplicateEx(ZyanString * destination,const ZyanStringView * source,ZyanUSize capacity,ZyanAllocator * allocator,ZyanU8 growth_factor,ZyanU8 shrink_threshold)135 ZyanStatus ZyanStringDuplicateEx(ZyanString* destination, const ZyanStringView* source,
136 ZyanUSize capacity, ZyanAllocator* allocator, ZyanU8 growth_factor, ZyanU8 shrink_threshold)
137 {
138 if (!source || !source->string.vector.size)
139 {
140 return ZYAN_STATUS_INVALID_ARGUMENT;
141 }
142
143 const ZyanUSize len = source->string.vector.size;
144 capacity = ZYAN_MAX(capacity, len - 1);
145 ZYAN_CHECK(ZyanStringInitEx(destination, capacity, allocator, growth_factor, shrink_threshold));
146 ZYAN_ASSERT(destination->vector.capacity >= len);
147
148 ZYAN_MEMCPY(destination->vector.data, source->string.vector.data,
149 source->string.vector.size - 1);
150 destination->vector.size = len;
151 ZYCORE_STRING_NULLTERMINATE(destination);
152
153 return ZYAN_STATUS_SUCCESS;
154 }
155
ZyanStringDuplicateCustomBuffer(ZyanString * destination,const ZyanStringView * source,char * buffer,ZyanUSize capacity)156 ZyanStatus ZyanStringDuplicateCustomBuffer(ZyanString* destination, const ZyanStringView* source,
157 char* buffer, ZyanUSize capacity)
158 {
159 if (!source || !source->string.vector.size)
160 {
161 return ZYAN_STATUS_INVALID_ARGUMENT;
162 }
163
164 const ZyanUSize len = source->string.vector.size;
165 if (capacity < len)
166 {
167 return ZYAN_STATUS_INSUFFICIENT_BUFFER_SIZE;
168 }
169
170 ZYAN_CHECK(ZyanStringInitCustomBuffer(destination, buffer, capacity));
171 ZYAN_ASSERT(destination->vector.capacity >= len);
172
173 ZYAN_MEMCPY(destination->vector.data, source->string.vector.data,
174 source->string.vector.size - 1);
175 destination->vector.size = len;
176 ZYCORE_STRING_NULLTERMINATE(destination);
177
178 return ZYAN_STATUS_SUCCESS;
179 }
180
181 /* ---------------------------------------------------------------------------------------------- */
182 /* Concatenation */
183 /* ---------------------------------------------------------------------------------------------- */
184
185 #ifndef ZYAN_NO_LIBC
186
ZyanStringConcat(ZyanString * destination,const ZyanStringView * s1,const ZyanStringView * s2,ZyanUSize capacity)187 ZyanStatus ZyanStringConcat(ZyanString* destination, const ZyanStringView* s1,
188 const ZyanStringView* s2, ZyanUSize capacity)
189 {
190 return ZyanStringConcatEx(destination, s1, s2, capacity, ZyanAllocatorDefault(),
191 ZYAN_STRING_DEFAULT_GROWTH_FACTOR, ZYAN_STRING_DEFAULT_SHRINK_THRESHOLD);
192 }
193
194 #endif // ZYAN_NO_LIBC
195
ZyanStringConcatEx(ZyanString * destination,const ZyanStringView * s1,const ZyanStringView * s2,ZyanUSize capacity,ZyanAllocator * allocator,ZyanU8 growth_factor,ZyanU8 shrink_threshold)196 ZyanStatus ZyanStringConcatEx(ZyanString* destination, const ZyanStringView* s1,
197 const ZyanStringView* s2, ZyanUSize capacity, ZyanAllocator* allocator, ZyanU8 growth_factor,
198 ZyanU8 shrink_threshold)
199 {
200 if (!s1 || !s2 || !s1->string.vector.size || !s2->string.vector.size)
201 {
202 return ZYAN_STATUS_INVALID_ARGUMENT;
203 }
204
205 const ZyanUSize len = s1->string.vector.size + s2->string.vector.size - 1;
206 capacity = ZYAN_MAX(capacity, len - 1);
207 ZYAN_CHECK(ZyanStringInitEx(destination, capacity, allocator, growth_factor, shrink_threshold));
208 ZYAN_ASSERT(destination->vector.capacity >= len);
209
210 ZYAN_MEMCPY(destination->vector.data, s1->string.vector.data, s1->string.vector.size - 1);
211 ZYAN_MEMCPY((char*)destination->vector.data + s1->string.vector.size - 1,
212 s2->string.vector.data, s2->string.vector.size - 1);
213 destination->vector.size = len;
214 ZYCORE_STRING_NULLTERMINATE(destination);
215
216 return ZYAN_STATUS_SUCCESS;
217 }
218
ZyanStringConcatCustomBuffer(ZyanString * destination,const ZyanStringView * s1,const ZyanStringView * s2,char * buffer,ZyanUSize capacity)219 ZyanStatus ZyanStringConcatCustomBuffer(ZyanString* destination, const ZyanStringView* s1,
220 const ZyanStringView* s2, char* buffer, ZyanUSize capacity)
221 {
222 if (!s1 || !s2 || !s1->string.vector.size || !s2->string.vector.size)
223 {
224 return ZYAN_STATUS_INVALID_ARGUMENT;
225 }
226
227 const ZyanUSize len = s1->string.vector.size + s2->string.vector.size - 1;
228 if (capacity < len)
229 {
230 return ZYAN_STATUS_INSUFFICIENT_BUFFER_SIZE;
231 }
232
233 ZYAN_CHECK(ZyanStringInitCustomBuffer(destination, buffer, capacity));
234 ZYAN_ASSERT(destination->vector.capacity >= len);
235
236 ZYAN_MEMCPY(destination->vector.data, s1->string.vector.data, s1->string.vector.size - 1);
237 ZYAN_MEMCPY((char*)destination->vector.data + s1->string.vector.size - 1,
238 s2->string.vector.data, s2->string.vector.size - 1);
239 destination->vector.size = len;
240 ZYCORE_STRING_NULLTERMINATE(destination);
241
242 return ZYAN_STATUS_SUCCESS;
243 }
244
245 /* ---------------------------------------------------------------------------------------------- */
246 /* Views */
247 /* ---------------------------------------------------------------------------------------------- */
248
ZyanStringViewInsideView(ZyanStringView * view,const ZyanStringView * source)249 ZyanStatus ZyanStringViewInsideView(ZyanStringView* view, const ZyanStringView* source)
250 {
251 if (!view || !source)
252 {
253 return ZYAN_STATUS_INVALID_ARGUMENT;
254 }
255
256 view->string.vector.data = source->string.vector.data;
257 view->string.vector.size = source->string.vector.size;
258
259 return ZYAN_STATUS_SUCCESS;
260 }
261
ZyanStringViewInsideViewEx(ZyanStringView * view,const ZyanStringView * source,ZyanUSize index,ZyanUSize count)262 ZyanStatus ZyanStringViewInsideViewEx(ZyanStringView* view, const ZyanStringView* source,
263 ZyanUSize index, ZyanUSize count)
264 {
265 if (!view || !source)
266 {
267 return ZYAN_STATUS_INVALID_ARGUMENT;
268 }
269
270 if (index + count >= source->string.vector.size)
271 {
272 return ZYAN_STATUS_OUT_OF_RANGE;
273 }
274
275 view->string.vector.data = (void*)((char*)source->string.vector.data + index);
276 view->string.vector.size = count;
277
278 return ZYAN_STATUS_SUCCESS;
279 }
280
ZyanStringViewInsideBuffer(ZyanStringView * view,const char * string)281 ZyanStatus ZyanStringViewInsideBuffer(ZyanStringView* view, const char* string)
282 {
283 if (!view || !string)
284 {
285 return ZYAN_STATUS_INVALID_ARGUMENT;
286 }
287
288 view->string.vector.data = (void*)string;
289 view->string.vector.size = ZYAN_STRLEN(string) + 1;
290
291 return ZYAN_STATUS_SUCCESS;
292 }
293
ZyanStringViewInsideBufferEx(ZyanStringView * view,const char * buffer,ZyanUSize length)294 ZyanStatus ZyanStringViewInsideBufferEx(ZyanStringView* view, const char* buffer, ZyanUSize length)
295 {
296 if (!view || !buffer || !length)
297 {
298 return ZYAN_STATUS_INVALID_ARGUMENT;
299 }
300
301 view->string.vector.data = (void*)buffer;
302 view->string.vector.size = length + 1;
303
304 return ZYAN_STATUS_SUCCESS;
305 }
306
ZyanStringViewGetSize(const ZyanStringView * view,ZyanUSize * size)307 ZyanStatus ZyanStringViewGetSize(const ZyanStringView* view, ZyanUSize* size)
308 {
309 if (!view || !size)
310 {
311 return ZYAN_STATUS_INVALID_ARGUMENT;
312 }
313
314 ZYAN_ASSERT(view->string.vector.size >= 1);
315 *size = view->string.vector.size - 1;
316
317 return ZYAN_STATUS_SUCCESS;
318 }
319
ZyanStringViewGetData(const ZyanStringView * view,const char ** buffer)320 ZYCORE_EXPORT ZyanStatus ZyanStringViewGetData(const ZyanStringView* view, const char** buffer)
321 {
322 if (!view || !buffer)
323 {
324 return ZYAN_STATUS_INVALID_ARGUMENT;
325 }
326
327 *buffer = view->string.vector.data;
328
329 return ZYAN_STATUS_SUCCESS;
330 }
331
332 /* ---------------------------------------------------------------------------------------------- */
333 /* Character access */
334 /* ---------------------------------------------------------------------------------------------- */
335
ZyanStringGetChar(const ZyanStringView * string,ZyanUSize index,char * value)336 ZyanStatus ZyanStringGetChar(const ZyanStringView* string, ZyanUSize index, char* value)
337 {
338 if (!string || !value)
339 {
340 return ZYAN_STATUS_INVALID_ARGUMENT;
341 }
342
343 // Don't allow direct access to the terminating '\0' character
344 if (index + 1 >= string->string.vector.size)
345 {
346 return ZYAN_STATUS_OUT_OF_RANGE;
347 }
348
349 const char* chr;
350 ZYAN_CHECK(ZyanVectorGetPointer(&string->string.vector, index, (const void**)&chr));
351 *value = *chr;
352
353 return ZYAN_STATUS_SUCCESS;
354 }
355
ZyanStringGetCharMutable(ZyanString * string,ZyanUSize index,char ** value)356 ZyanStatus ZyanStringGetCharMutable(ZyanString* string, ZyanUSize index, char** value)
357 {
358 if (!string)
359 {
360 return ZYAN_STATUS_INVALID_ARGUMENT;
361 }
362
363 // Don't allow direct access to the terminating '\0' character
364 if (index + 1 >= string->vector.size)
365 {
366 return ZYAN_STATUS_OUT_OF_RANGE;
367 }
368
369 return ZyanVectorGetPointerMutable(&string->vector, index, (void**)value);
370 }
371
ZyanStringSetChar(ZyanString * string,ZyanUSize index,char value)372 ZyanStatus ZyanStringSetChar(ZyanString* string, ZyanUSize index, char value)
373 {
374 if (!string)
375 {
376 return ZYAN_STATUS_INVALID_ARGUMENT;
377 }
378
379 // Don't allow direct access to the terminating '\0' character
380 if (index + 1 >= string->vector.size)
381 {
382 return ZYAN_STATUS_OUT_OF_RANGE;
383 }
384
385 return ZyanVectorSet(&string->vector, index, (void*)&value);
386 }
387
388 /* ---------------------------------------------------------------------------------------------- */
389 /* Insertion */
390 /* ---------------------------------------------------------------------------------------------- */
391
ZyanStringInsert(ZyanString * destination,ZyanUSize index,const ZyanStringView * source)392 ZyanStatus ZyanStringInsert(ZyanString* destination, ZyanUSize index, const ZyanStringView* source)
393 {
394 if (!destination || !source || !source->string.vector.size)
395 {
396 return ZYAN_STATUS_INVALID_ARGUMENT;
397 }
398
399 if (index == destination->vector.size)
400 {
401 return ZyanStringAppend(destination, source);
402 }
403
404 // Don't allow insertion after the terminating '\0' character
405 if (index >= destination->vector.size)
406 {
407 return ZYAN_STATUS_OUT_OF_RANGE;
408 }
409
410 ZYAN_CHECK(ZyanVectorInsertRange(&destination->vector, index, source->string.vector.data,
411 source->string.vector.size - 1));
412 ZYCORE_STRING_ASSERT_NULLTERMINATION(destination);
413
414 return ZYAN_STATUS_SUCCESS;
415 }
416
ZyanStringInsertEx(ZyanString * destination,ZyanUSize destination_index,const ZyanStringView * source,ZyanUSize source_index,ZyanUSize count)417 ZyanStatus ZyanStringInsertEx(ZyanString* destination, ZyanUSize destination_index,
418 const ZyanStringView* source, ZyanUSize source_index, ZyanUSize count)
419 {
420 if (!destination || !source || !source->string.vector.size)
421 {
422 return ZYAN_STATUS_INVALID_ARGUMENT;
423 }
424
425 if (destination_index == destination->vector.size)
426 {
427 return ZyanStringAppendEx(destination, source, source_index, count);
428 }
429
430 // Don't allow insertion after the terminating '\0' character
431 if (destination_index >= destination->vector.size)
432 {
433 return ZYAN_STATUS_OUT_OF_RANGE;
434 }
435
436 // Don't allow access to the terminating '\0' character
437 if (source_index + count >= source->string.vector.size)
438 {
439 return ZYAN_STATUS_OUT_OF_RANGE;
440 }
441
442 ZYAN_CHECK(ZyanVectorInsertRange(&destination->vector, destination_index,
443 (char*)source->string.vector.data + source_index, count));
444 ZYCORE_STRING_ASSERT_NULLTERMINATION(destination);
445
446 return ZYAN_STATUS_SUCCESS;
447 }
448
449 /* ---------------------------------------------------------------------------------------------- */
450 /* Appending */
451 /* ---------------------------------------------------------------------------------------------- */
452
ZyanStringAppend(ZyanString * destination,const ZyanStringView * source)453 ZyanStatus ZyanStringAppend(ZyanString* destination, const ZyanStringView* source)
454 {
455 if (!destination || !source || !source->string.vector.size)
456 {
457 return ZYAN_STATUS_INVALID_ARGUMENT;
458 }
459
460 const ZyanUSize len = destination->vector.size;
461 ZYAN_CHECK(ZyanVectorResize(&destination->vector, len + source->string.vector.size - 1));
462 ZYAN_MEMCPY((char*)destination->vector.data + len - 1, source->string.vector.data,
463 source->string.vector.size - 1);
464 ZYCORE_STRING_NULLTERMINATE(destination);
465
466 return ZYAN_STATUS_SUCCESS;
467 }
468
ZyanStringAppendEx(ZyanString * destination,const ZyanStringView * source,ZyanUSize source_index,ZyanUSize count)469 ZyanStatus ZyanStringAppendEx(ZyanString* destination, const ZyanStringView* source,
470 ZyanUSize source_index, ZyanUSize count)
471 {
472 if (!destination || !source || !source->string.vector.size)
473 {
474 return ZYAN_STATUS_INVALID_ARGUMENT;
475 }
476
477 // Don't allow access to the terminating '\0' character
478 if (source_index + count >= source->string.vector.size)
479 {
480 return ZYAN_STATUS_OUT_OF_RANGE;
481 }
482
483 const ZyanUSize len = destination->vector.size;
484 ZYAN_CHECK(ZyanVectorResize(&destination->vector, len + count));
485 ZYAN_MEMCPY((char*)destination->vector.data + len - 1,
486 (const char*)source->string.vector.data + source_index, count);
487 ZYCORE_STRING_NULLTERMINATE(destination);
488
489 return ZYAN_STATUS_SUCCESS;
490 }
491
492 /* ---------------------------------------------------------------------------------------------- */
493 /* Deletion */
494 /* ---------------------------------------------------------------------------------------------- */
495
ZyanStringDelete(ZyanString * string,ZyanUSize index,ZyanUSize count)496 ZyanStatus ZyanStringDelete(ZyanString* string, ZyanUSize index, ZyanUSize count)
497 {
498 if (!string)
499 {
500 return ZYAN_STATUS_INVALID_ARGUMENT;
501 }
502
503 // Don't allow removal of the terminating '\0' character
504 if (index + count >= string->vector.size)
505 {
506 return ZYAN_STATUS_OUT_OF_RANGE;
507 }
508
509 ZYAN_CHECK(ZyanVectorDeleteRange(&string->vector, index, count));
510 ZYCORE_STRING_NULLTERMINATE(string);
511
512 return ZYAN_STATUS_SUCCESS;
513 }
514
ZyanStringTruncate(ZyanString * string,ZyanUSize index)515 ZyanStatus ZyanStringTruncate(ZyanString* string, ZyanUSize index)
516 {
517 if (!string)
518 {
519 return ZYAN_STATUS_INVALID_ARGUMENT;
520 }
521
522 // Don't allow removal of the terminating '\0' character
523 if (index >= string->vector.size)
524 {
525 return ZYAN_STATUS_OUT_OF_RANGE;
526 }
527
528 ZYAN_CHECK(ZyanVectorDeleteRange(&string->vector, index, string->vector.size - index - 1));
529 ZYCORE_STRING_NULLTERMINATE(string);
530
531 return ZYAN_STATUS_SUCCESS;
532 }
533
ZyanStringClear(ZyanString * string)534 ZyanStatus ZyanStringClear(ZyanString* string)
535 {
536 if (!string)
537 {
538 return ZYAN_STATUS_INVALID_ARGUMENT;
539 }
540
541 ZYAN_CHECK(ZyanVectorClear(&string->vector));
542 // `ZyanVector` guarantees a minimum capacity of 1 element/character
543 ZYAN_ASSERT(string->vector.capacity >= 1);
544
545 *(char*)string->vector.data = '\0';
546 string->vector.size++;
547
548 return ZYAN_STATUS_SUCCESS;
549 }
550
551 /* ---------------------------------------------------------------------------------------------- */
552 /* Searching */
553 /* ---------------------------------------------------------------------------------------------- */
554
ZyanStringLPos(const ZyanStringView * haystack,const ZyanStringView * needle,ZyanISize * found_index)555 ZyanStatus ZyanStringLPos(const ZyanStringView* haystack, const ZyanStringView* needle,
556 ZyanISize* found_index)
557 {
558 if (!haystack)
559 {
560 return ZYAN_STATUS_INVALID_ARGUMENT;
561 }
562
563 return ZyanStringLPosEx(haystack, needle, found_index, 0, haystack->string.vector.size - 1);
564 }
565
ZyanStringLPosEx(const ZyanStringView * haystack,const ZyanStringView * needle,ZyanISize * found_index,ZyanUSize index,ZyanUSize count)566 ZyanStatus ZyanStringLPosEx(const ZyanStringView* haystack, const ZyanStringView* needle,
567 ZyanISize* found_index, ZyanUSize index, ZyanUSize count)
568 {
569 if (!haystack || !needle || !found_index)
570 {
571 return ZYAN_STATUS_INVALID_ARGUMENT;
572 }
573
574 // Don't allow access to the terminating '\0' character
575 if (index + count >= haystack->string.vector.size)
576 {
577 return ZYAN_STATUS_OUT_OF_RANGE;
578 }
579
580 if ((haystack->string.vector.size == 1) || (needle->string.vector.size == 1) ||
581 (haystack->string.vector.size < needle->string.vector.size))
582 {
583 *found_index = -1;
584 return ZYAN_STATUS_FALSE;
585 }
586
587 const char* s = (const char*)haystack->string.vector.data + index;
588 const char* b = (const char*)needle->string.vector.data;
589 for (; s + 1 < (const char*)haystack->string.vector.data + haystack->string.vector.size; ++s)
590 {
591 if (*s != *b)
592 {
593 continue;
594 }
595 const char* a = s;
596 for (;;)
597 {
598 if ((ZyanUSize)(a - (const char*)haystack->string.vector.data) > index + count)
599 {
600 *found_index = -1;
601 return ZYAN_STATUS_FALSE;
602 }
603 if (*b == 0)
604 {
605 *found_index = (ZyanISize)(s - (const char*)haystack->string.vector.data);
606 return ZYAN_STATUS_TRUE;
607 }
608 if (*a++ != *b++)
609 {
610 break;
611 }
612 }
613 b = (char*)needle->string.vector.data;
614 }
615
616 *found_index = -1;
617 return ZYAN_STATUS_FALSE;
618 }
619
ZyanStringLPosI(const ZyanStringView * haystack,const ZyanStringView * needle,ZyanISize * found_index)620 ZyanStatus ZyanStringLPosI(const ZyanStringView* haystack, const ZyanStringView* needle,
621 ZyanISize* found_index)
622 {
623 if (!haystack)
624 {
625 return ZYAN_STATUS_INVALID_ARGUMENT;
626 }
627
628 return ZyanStringLPosIEx(haystack, needle, found_index, 0, haystack->string.vector.size - 1);
629 }
630
ZyanStringLPosIEx(const ZyanStringView * haystack,const ZyanStringView * needle,ZyanISize * found_index,ZyanUSize index,ZyanUSize count)631 ZyanStatus ZyanStringLPosIEx(const ZyanStringView* haystack, const ZyanStringView* needle,
632 ZyanISize* found_index, ZyanUSize index, ZyanUSize count)
633 {
634 // This solution assumes that characters are represented using ASCII representation, i.e.,
635 // codes for 'a', 'b', 'c', .. 'z' are 97, 98, 99, .. 122 respectively. And codes for 'A',
636 // 'B', 'C', .. 'Z' are 65, 66, .. 95 respectively.
637
638 if (!haystack || !needle || !found_index)
639 {
640 return ZYAN_STATUS_INVALID_ARGUMENT;
641 }
642
643 // Don't allow access to the terminating '\0' character
644 if (index + count >= haystack->string.vector.size)
645 {
646 return ZYAN_STATUS_OUT_OF_RANGE;
647 }
648
649 if ((haystack->string.vector.size == 1) || (needle->string.vector.size == 1) ||
650 (haystack->string.vector.size < needle->string.vector.size))
651 {
652 *found_index = -1;
653 return ZYAN_STATUS_FALSE;
654 }
655
656 const char* s = (const char*)haystack->string.vector.data + index;
657 const char* b = (const char*)needle->string.vector.data;
658 for (; s + 1 < (const char*)haystack->string.vector.data + haystack->string.vector.size; ++s)
659 {
660 if ((*s != *b) && ((*s ^ 32) != *b))
661 {
662 continue;
663 }
664 const char* a = s;
665 for (;;)
666 {
667 if ((ZyanUSize)(a - (const char*)haystack->string.vector.data) > index + count)
668 {
669 *found_index = -1;
670 return ZYAN_STATUS_FALSE;
671 }
672 if (*b == 0)
673 {
674 *found_index = (ZyanISize)(s - (const char*)haystack->string.vector.data);
675 return ZYAN_STATUS_TRUE;
676 }
677 const char c1 = *a++;
678 const char c2 = *b++;
679 if ((c1 != c2) && ((c1 ^ 32) != c2))
680 {
681 break;
682 }
683 }
684 b = (char*)needle->string.vector.data;
685 }
686
687 *found_index = -1;
688 return ZYAN_STATUS_FALSE;
689 }
690
ZyanStringRPos(const ZyanStringView * haystack,const ZyanStringView * needle,ZyanISize * found_index)691 ZyanStatus ZyanStringRPos(const ZyanStringView* haystack, const ZyanStringView* needle,
692 ZyanISize* found_index)
693 {
694 if (!haystack)
695 {
696 return ZYAN_STATUS_INVALID_ARGUMENT;
697 }
698
699 return ZyanStringRPosEx(haystack, needle, found_index, haystack->string.vector.size - 1,
700 haystack->string.vector.size - 1);
701 }
702
ZyanStringRPosEx(const ZyanStringView * haystack,const ZyanStringView * needle,ZyanISize * found_index,ZyanUSize index,ZyanUSize count)703 ZyanStatus ZyanStringRPosEx(const ZyanStringView* haystack, const ZyanStringView* needle,
704 ZyanISize* found_index, ZyanUSize index, ZyanUSize count)
705 {
706 if (!haystack || !needle || !found_index)
707 {
708 return ZYAN_STATUS_INVALID_ARGUMENT;
709 }
710
711 // Don't allow access to the terminating '\0' character
712 if ((index >= haystack->string.vector.size) || (count > index))
713 {
714 return ZYAN_STATUS_OUT_OF_RANGE;
715 }
716
717 if (!index || !count ||
718 (haystack->string.vector.size == 1) || (needle->string.vector.size == 1) ||
719 (haystack->string.vector.size < needle->string.vector.size))
720 {
721 *found_index = -1;
722 return ZYAN_STATUS_FALSE;
723 }
724
725 const char* s = (const char*)haystack->string.vector.data + index - 1;
726 const char* b = (const char*)needle->string.vector.data + needle->string.vector.size - 2;
727 for (; s >= (const char*)haystack->string.vector.data; --s)
728 {
729 if (*s != *b)
730 {
731 continue;
732 }
733 const char* a = s;
734 for (;;)
735 {
736 if (b < (const char*)needle->string.vector.data)
737 {
738 *found_index = (ZyanISize)(a - (const char*)haystack->string.vector.data + 1);
739 return ZYAN_STATUS_TRUE;
740 }
741 if (a < (const char*)haystack->string.vector.data + index - count)
742 {
743 *found_index = -1;
744 return ZYAN_STATUS_FALSE;
745 }
746 if (*a-- != *b--)
747 {
748 break;
749 }
750 }
751 b = (char*)needle->string.vector.data + needle->string.vector.size - 2;
752 }
753
754 *found_index = -1;
755 return ZYAN_STATUS_FALSE;
756 }
757
ZyanStringRPosI(const ZyanStringView * haystack,const ZyanStringView * needle,ZyanISize * found_index)758 ZyanStatus ZyanStringRPosI(const ZyanStringView* haystack, const ZyanStringView* needle,
759 ZyanISize* found_index)
760 {
761 if (!haystack)
762 {
763 return ZYAN_STATUS_INVALID_ARGUMENT;
764 }
765
766 return ZyanStringRPosIEx(haystack, needle, found_index, haystack->string.vector.size - 1,
767 haystack->string.vector.size - 1);
768 }
769
ZyanStringRPosIEx(const ZyanStringView * haystack,const ZyanStringView * needle,ZyanISize * found_index,ZyanUSize index,ZyanUSize count)770 ZyanStatus ZyanStringRPosIEx(const ZyanStringView* haystack, const ZyanStringView* needle,
771 ZyanISize* found_index, ZyanUSize index, ZyanUSize count)
772 {
773 // This solution assumes that characters are represented using ASCII representation, i.e.,
774 // codes for 'a', 'b', 'c', .. 'z' are 97, 98, 99, .. 122 respectively. And codes for 'A',
775 // 'B', 'C', .. 'Z' are 65, 66, .. 95 respectively.
776
777 if (!haystack || !needle || !found_index)
778 {
779 return ZYAN_STATUS_INVALID_ARGUMENT;
780 }
781
782 // Don't allow access to the terminating '\0' character
783 if ((index >= haystack->string.vector.size) || (count > index))
784 {
785 return ZYAN_STATUS_OUT_OF_RANGE;
786 }
787
788 if (!index || !count ||
789 (haystack->string.vector.size == 1) || (needle->string.vector.size == 1) ||
790 (haystack->string.vector.size < needle->string.vector.size))
791 {
792 *found_index = -1;
793 return ZYAN_STATUS_FALSE;
794 }
795
796 const char* s = (const char*)haystack->string.vector.data + index - 1;
797 const char* b = (const char*)needle->string.vector.data + needle->string.vector.size - 2;
798 for (; s >= (const char*)haystack->string.vector.data; --s)
799 {
800 if ((*s != *b) && ((*s ^ 32) != *b))
801 {
802 continue;
803 }
804 const char* a = s;
805 for (;;)
806 {
807 if (b < (const char*)needle->string.vector.data)
808 {
809 *found_index = (ZyanISize)(a - (const char*)haystack->string.vector.data + 1);
810 return ZYAN_STATUS_TRUE;
811 }
812 if (a < (const char*)haystack->string.vector.data + index - count)
813 {
814 *found_index = -1;
815 return ZYAN_STATUS_FALSE;
816 }
817 const char c1 = *a--;
818 const char c2 = *b--;
819 if ((c1 != c2) && ((c1 ^ 32) != c2))
820 {
821 break;
822 }
823 }
824 b = (char*)needle->string.vector.data + needle->string.vector.size - 2;
825 }
826
827 *found_index = -1;
828 return ZYAN_STATUS_FALSE;
829 }
830
831 /* ---------------------------------------------------------------------------------------------- */
832 /* Comparing */
833 /* ---------------------------------------------------------------------------------------------- */
834
ZyanStringCompare(const ZyanStringView * s1,const ZyanStringView * s2,ZyanI32 * result)835 ZyanStatus ZyanStringCompare(const ZyanStringView* s1, const ZyanStringView* s2, ZyanI32* result)
836 {
837 if (!s1 || !s2)
838 {
839 return ZYAN_STATUS_INVALID_ARGUMENT;
840 }
841
842 if (s1->string.vector.size < s2->string.vector.size)
843 {
844 *result = -1;
845 return ZYAN_STATUS_FALSE;
846 }
847 if (s1->string.vector.size > s2->string.vector.size)
848 {
849 *result = 1;
850 return ZYAN_STATUS_FALSE;
851 }
852
853 const char* const a = (char*)s1->string.vector.data;
854 const char* const b = (char*)s2->string.vector.data;
855 ZyanUSize i;
856 for (i = 0; (i + 1 < s1->string.vector.size) && (i + 1 < s2->string.vector.size); ++i)
857 {
858 if (a[i] == b[i])
859 {
860 continue;
861 }
862 break;
863 }
864
865 if (a[i] == b[i])
866 {
867 *result = 0;
868 return ZYAN_STATUS_TRUE;
869 }
870
871 if ((a[i] | 32) < (b[i] | 32))
872 {
873 *result = -1;
874 return ZYAN_STATUS_FALSE;
875 }
876
877 *result = 1;
878 return ZYAN_STATUS_FALSE;
879 }
880
ZyanStringCompareI(const ZyanStringView * s1,const ZyanStringView * s2,ZyanI32 * result)881 ZyanStatus ZyanStringCompareI(const ZyanStringView* s1, const ZyanStringView* s2, ZyanI32* result)
882 {
883 // This solution assumes that characters are represented using ASCII representation, i.e.,
884 // codes for 'a', 'b', 'c', .. 'z' are 97, 98, 99, .. 122 respectively. And codes for 'A',
885 // 'B', 'C', .. 'Z' are 65, 66, .. 95 respectively.
886
887 if (!s1 || !s2)
888 {
889 return ZYAN_STATUS_INVALID_ARGUMENT;
890 }
891
892 if (s1->string.vector.size < s2->string.vector.size)
893 {
894 *result = -1;
895 return ZYAN_STATUS_FALSE;
896 }
897 if (s1->string.vector.size > s2->string.vector.size)
898 {
899 *result = 1;
900 return ZYAN_STATUS_FALSE;
901 }
902
903 const char* const a = (char*)s1->string.vector.data;
904 const char* const b = (char*)s2->string.vector.data;
905 ZyanUSize i;
906 for (i = 0; (i + 1 < s1->string.vector.size) && (i + 1 < s2->string.vector.size); ++i)
907 {
908 if ((a[i] == b[i]) || ((a[i] ^ 32) == b[i]))
909 {
910 continue;
911 }
912 break;
913 }
914
915 if (a[i] == b[i])
916 {
917 *result = 0;
918 return ZYAN_STATUS_TRUE;
919 }
920
921 if ((a[i] | 32) < (b[i] | 32))
922 {
923 *result = -1;
924 return ZYAN_STATUS_FALSE;
925 }
926
927 *result = 1;
928 return ZYAN_STATUS_FALSE;
929 }
930
931 /* ---------------------------------------------------------------------------------------------- */
932 /* Case conversion */
933 /* ---------------------------------------------------------------------------------------------- */
934
ZyanStringToLowerCase(ZyanString * string)935 ZyanStatus ZyanStringToLowerCase(ZyanString* string)
936 {
937 if (!string)
938 {
939 return ZYAN_STATUS_INVALID_ARGUMENT;
940 }
941
942 return ZyanStringToLowerCaseEx(string, 0, string->vector.size - 1);
943 }
944
ZyanStringToLowerCaseEx(ZyanString * string,ZyanUSize index,ZyanUSize count)945 ZyanStatus ZyanStringToLowerCaseEx(ZyanString* string, ZyanUSize index, ZyanUSize count)
946 {
947 // This solution assumes that characters are represented using ASCII representation, i.e.,
948 // codes for 'a', 'b', 'c', .. 'z' are 97, 98, 99, .. 122 respectively. And codes for 'A',
949 // 'B', 'C', .. 'Z' are 65, 66, .. 95 respectively.
950
951 if (!string)
952 {
953 return ZYAN_STATUS_INVALID_ARGUMENT;
954 }
955
956 // Don't allow access to the terminating '\0' character
957 if (index + count >= string->vector.size)
958 {
959 return ZYAN_STATUS_OUT_OF_RANGE;
960 }
961
962 char* s = (char*)string->vector.data + index;
963 for (ZyanUSize i = index; i < index + count; ++i)
964 {
965 const char c = *s;
966 if ((c >= 'A') && (c <= 'Z'))
967 {
968 *s = c | 32;
969 }
970 ++s;
971 }
972
973 return ZYAN_STATUS_SUCCESS;
974 }
975
ZyanStringToUpperCase(ZyanString * string)976 ZyanStatus ZyanStringToUpperCase(ZyanString* string)
977 {
978 if (!string)
979 {
980 return ZYAN_STATUS_INVALID_ARGUMENT;
981 }
982
983 return ZyanStringToUpperCaseEx(string, 0, string->vector.size - 1);
984 }
985
ZyanStringToUpperCaseEx(ZyanString * string,ZyanUSize index,ZyanUSize count)986 ZyanStatus ZyanStringToUpperCaseEx(ZyanString* string, ZyanUSize index, ZyanUSize count)
987 {
988 // This solution assumes that characters are represented using ASCII representation, i.e.,
989 // codes for 'a', 'b', 'c', .. 'z' are 97, 98, 99, .. 122 respectively. And codes for 'A',
990 // 'B', 'C', .. 'Z' are 65, 66, .. 95 respectively.
991
992 if (!string)
993 {
994 return ZYAN_STATUS_INVALID_ARGUMENT;
995 }
996
997 // Don't allow access to the terminating '\0' character
998 if (index + count >= string->vector.size)
999 {
1000 return ZYAN_STATUS_OUT_OF_RANGE;
1001 }
1002
1003 char* s = (char*)string->vector.data + index;
1004 for (ZyanUSize i = index; i < index + count; ++i)
1005 {
1006 const char c = *s;
1007 if ((c >= 'a') && (c <= 'z'))
1008 {
1009 *s = c & ~32;
1010 }
1011 ++s;
1012 }
1013
1014 return ZYAN_STATUS_SUCCESS;
1015 }
1016
1017 /* ---------------------------------------------------------------------------------------------- */
1018 /* Memory management */
1019 /* ---------------------------------------------------------------------------------------------- */
1020
ZyanStringResize(ZyanString * string,ZyanUSize size)1021 ZyanStatus ZyanStringResize(ZyanString* string, ZyanUSize size)
1022 {
1023 if (!string)
1024 {
1025 return ZYAN_STATUS_INVALID_ARGUMENT;
1026 }
1027
1028 ZYAN_CHECK(ZyanVectorResize(&string->vector, size + 1));
1029 ZYCORE_STRING_NULLTERMINATE(string);
1030
1031 return ZYAN_STATUS_SUCCESS;
1032 }
1033
ZyanStringReserve(ZyanString * string,ZyanUSize capacity)1034 ZyanStatus ZyanStringReserve(ZyanString* string, ZyanUSize capacity)
1035 {
1036 if (!string)
1037 {
1038 return ZYAN_STATUS_INVALID_ARGUMENT;
1039 }
1040
1041 return ZyanVectorReserve(&string->vector, capacity);
1042 }
1043
ZyanStringShrinkToFit(ZyanString * string)1044 ZyanStatus ZyanStringShrinkToFit(ZyanString* string)
1045 {
1046 if (!string)
1047 {
1048 return ZYAN_STATUS_INVALID_ARGUMENT;
1049 }
1050
1051 return ZyanVectorShrinkToFit(&string->vector);
1052 }
1053
1054 /* ---------------------------------------------------------------------------------------------- */
1055 /* Information */
1056 /* ---------------------------------------------------------------------------------------------- */
1057
ZyanStringGetCapacity(const ZyanString * string,ZyanUSize * capacity)1058 ZyanStatus ZyanStringGetCapacity(const ZyanString* string, ZyanUSize* capacity)
1059 {
1060 if (!string)
1061 {
1062 return ZYAN_STATUS_INVALID_ARGUMENT;
1063 }
1064
1065 ZYAN_ASSERT(string->vector.capacity >= 1);
1066 *capacity = string->vector.capacity - 1;
1067
1068 return ZYAN_STATUS_SUCCESS;
1069 }
1070
ZyanStringGetSize(const ZyanString * string,ZyanUSize * size)1071 ZyanStatus ZyanStringGetSize(const ZyanString* string, ZyanUSize* size)
1072 {
1073 if (!string)
1074 {
1075 return ZYAN_STATUS_INVALID_ARGUMENT;
1076 }
1077
1078 ZYAN_ASSERT(string->vector.size >= 1);
1079 *size = string->vector.size - 1;
1080
1081 return ZYAN_STATUS_SUCCESS;
1082 }
1083
ZyanStringGetData(const ZyanString * string,const char ** value)1084 ZyanStatus ZyanStringGetData(const ZyanString* string, const char** value)
1085 {
1086 if (!string)
1087 {
1088 return ZYAN_STATUS_INVALID_ARGUMENT;
1089 }
1090
1091 *value = string->vector.data;
1092
1093 return ZYAN_STATUS_SUCCESS;
1094 }
1095
1096 /* ---------------------------------------------------------------------------------------------- */
1097
1098 /* ============================================================================================== */
1099