For a given text, initialize the first letter after a word-separator and lowercase the others e.g: - "IT is a tEXt str" -> "It Is A Text Str"
| 513 | // the others e.g: |
| 514 | // - "IT is a tEXt str" -> "It Is A Text Str" |
| 515 | GANDIVA_EXPORT |
| 516 | const char* gdv_fn_initcap_utf8(int64_t context, const char* data, int32_t data_len, |
| 517 | int32_t* out_len) { |
| 518 | if (data_len == 0) { |
| 519 | *out_len = data_len; |
| 520 | return ""; |
| 521 | } |
| 522 | |
| 523 | int32_t alloc_length = 0; |
| 524 | if (ARROW_PREDICT_FALSE(!is_datalen_valid(context, data_len, &alloc_length, out_len))) { |
| 525 | return ""; |
| 526 | } |
| 527 | |
| 528 | // If it is a single-byte character (ASCII), corresponding uppercase is always 1-byte |
| 529 | // long; if it is >= 2 bytes long, uppercase can be at most 4 bytes long, so length of |
| 530 | // the output can be at most twice the length of the input |
| 531 | char* out = reinterpret_cast<char*>(gdv_fn_context_arena_malloc(context, alloc_length)); |
| 532 | if (out == nullptr) { |
| 533 | gdv_fn_context_set_error_msg(context, "Could not allocate memory for output string"); |
| 534 | *out_len = 0; |
| 535 | return ""; |
| 536 | } |
| 537 | |
| 538 | int32_t char_len = 0; |
| 539 | int32_t out_char_len = 0; |
| 540 | int32_t out_idx = 0; |
| 541 | uint32_t char_codepoint; |
| 542 | |
| 543 | // Any character is considered as space, except if it is alphanumeric |
| 544 | bool last_char_was_space = true; |
| 545 | |
| 546 | for (int32_t i = 0; i < data_len; i += char_len) { |
| 547 | // An optimization for single byte characters: |
| 548 | if (static_cast<signed char>(data[i]) >= 0) { // 1-byte char (0x00 ~ 0x7F) |
| 549 | char_len = 1; |
| 550 | char cur = data[i]; |
| 551 | |
| 552 | if (cur >= 0x61 && cur <= 0x7a && last_char_was_space) { |
| 553 | // Check if the character is the first one of the word and it is |
| 554 | // lowercase -> 'a' - 'z' : 0x61 - 0x7a. |
| 555 | // Then turn it into uppercase -> 'A' - 'Z' : 0x41 - 0x5a |
| 556 | out[out_idx++] = static_cast<char>(cur - 0x20); |
| 557 | last_char_was_space = false; |
| 558 | } else if (cur >= 0x41 && cur <= 0x5a && !last_char_was_space) { |
| 559 | out[out_idx++] = static_cast<char>(cur + 0x20); |
| 560 | } else { |
| 561 | // Check if the ASCII character is not an alphanumeric character: |
| 562 | // '0' - '9': 0x30 - 0x39 |
| 563 | // 'a' - 'z' : 0x61 - 0x7a |
| 564 | // 'A' - 'Z' : 0x41 - 0x5a |
| 565 | last_char_was_space = (cur < 0x30) || (cur > 0x39 && cur < 0x41) || |
| 566 | (cur > 0x5a && cur < 0x61) || (cur > 0x7a); |
| 567 | out[out_idx++] = cur; |
| 568 | } |
| 569 | continue; |
| 570 | } |
| 571 | |
| 572 | char_len = gdv_fn_utf8_char_length(data[i]); |