1#define _GNU_SOURCE  // for memrchr
  2#include <string.h>
  3
  4#include "include/pw.h"
  5#include "src/types/string/string_internal.h"
  6
  7
  8static uint8_t* strrchr2_1(uint8_t* start_ptr, uint8_t* end_ptr, char32_t codepoint, uint8_t* char_size)
  9{
 10    *char_size = 1;
 11    return memrchr(start_ptr, (uint8_t) codepoint, end_ptr - start_ptr);
 12}
 13
 14static uint8_t* strrchr2_2(uint8_t* start_ptr, uint8_t* end_ptr, char32_t codepoint, uint8_t* char_size)
 15{
 16    char32_t width = 0;
 17    while (start_ptr < end_ptr) {
 18        end_ptr -= 2;
 19        char32_t c = *(uint16_t*) end_ptr;
 20        width |= c;
 21        if (c == codepoint) {
 22            *char_size = calc_char_size(width);
 23            return end_ptr;
 24        }
 25    }
 26    *char_size = calc_char_size(width);
 27    return nullptr;
 28}
 29
 30static uint8_t* strrchr2_3(uint8_t* start_ptr, uint8_t* end_ptr, char32_t codepoint, uint8_t* char_size)
 31{
 32    char32_t width = 0;
 33    while (start_ptr < end_ptr) {
 34        end_ptr -= 3;
 35        char32_t c = *end_ptr++;
 36        c |= (*end_ptr++) << 8;
 37        c |= (*end_ptr++) << 16;
 38        end_ptr -= 3;
 39        width |= c;
 40        if (c == codepoint) {
 41            *char_size = calc_char_size(width);
 42            return end_ptr;
 43        }
 44    }
 45    *char_size = calc_char_size(width);
 46    return nullptr;
 47}
 48
 49static uint8_t* strrchr2_4(uint8_t* start_ptr, uint8_t* end_ptr, char32_t codepoint, uint8_t* char_size)
 50{
 51    char32_t width = 0;
 52    while (start_ptr < end_ptr) {
 53        end_ptr -= 4;
 54        char32_t c = *(char32_t*) end_ptr;
 55        width |= c;
 56        if (c == codepoint) {
 57            *char_size = calc_char_size(width);
 58            return end_ptr;
 59        }
 60    }
 61    *char_size = calc_char_size(width);
 62    return nullptr;
 63}
 64
 65StrRChr2 _pw_strrchr2_variants[5] = {
 66    nullptr,
 67    strrchr2_1,
 68    strrchr2_2,
 69    strrchr2_3,
 70    strrchr2_4
 71};
 72
 73[[nodiscard]] bool pw_strrchr2(PwValuePtr str, char32_t chr, unsigned end_pos, unsigned* result, uint8_t* max_char_size)
 74{
 75    pw_assert(pw_is_string(str));
 76    uint8_t char_size = str->str_params.char_size;
 77    if (_pw_unlikely(char_size < calc_char_size(chr))) {
 78        return false;
 79    }
 80    unsigned length;
 81    uint8_t* start_ptr = _pw_string_start_length(str, &length);
 82
 83    if (_pw_unlikely(length == 0)) {
 84        *max_char_size = 1;
 85        return false;
 86    }
 87    if (end_pos > length) {
 88        end_pos = length;
 89    }
 90    uint8_t* end_ptr = start_ptr + end_pos * char_size;
 91    StrChr2 fn_strrchr2 = _pw_strrchr2_variants[char_size];
 92    uint8_t* char_ptr = fn_strrchr2(start_ptr, end_ptr, chr, max_char_size);
 93    if (_pw_unlikely(!char_ptr)) {
 94        return false;
 95    }
 96    if (result) {
 97        *result = (char_ptr - start_ptr) / char_size;
 98    }
 99    return true;
100}