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 "zydis/Zycore/String.h"
28 #include "zydis/Zycore/LibC.h"
29 
30 /* ============================================================================================== */
31 /* Internal macros                                                                                */
32 /* ============================================================================================== */
33 
34 /**
35  * @brief   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  * @brief   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,float growth_factor,float shrink_threshold)64 ZyanStatus ZyanStringInitEx(ZyanString* string, ZyanUSize capacity, ZyanAllocator* allocator,
65     float growth_factor, float 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,float growth_factor,float shrink_threshold)135 ZyanStatus ZyanStringDuplicateEx(ZyanString* destination, const ZyanStringView* source,
136     ZyanUSize capacity, ZyanAllocator* allocator, float growth_factor, float 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,float growth_factor,float shrink_threshold)196 ZyanStatus ZyanStringConcatEx(ZyanString* destination, const ZyanStringView* s1,
197     const ZyanStringView* s2, ZyanUSize capacity, ZyanAllocator* allocator, float growth_factor,
198     float 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