Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
stenzek
GitHub Repository: stenzek/duckstation
Path: blob/master/dep/zydis/dependencies/zycore/src/String.c
4808 views
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
1100