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