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* strrchr_1(uint8_t* start_ptr, uint8_t* end_ptr, char32_t codepoint)
 9{
10    return memrchr(start_ptr, (uint8_t) codepoint, end_ptr - start_ptr);
11}
12
13static uint8_t* strrchr_2(uint8_t* start_ptr, uint8_t* end_ptr, char32_t codepoint)
14{
15    while (start_ptr < end_ptr) {
16        end_ptr -= 2;
17        char32_t c = *(uint16_t*) end_ptr;
18        if (c == codepoint) {
19            return end_ptr;
20        }
21    }
22    return nullptr;
23}
24
25static uint8_t* strrchr_3(uint8_t* start_ptr, uint8_t* end_ptr, char32_t codepoint)
26{
27    while (start_ptr < end_ptr) {
28        end_ptr -= 3;
29        char32_t c = *end_ptr++;
30        c |= (*end_ptr++) << 8;
31        c |= (*end_ptr++) << 16;
32        end_ptr -= 3;
33        if (c == codepoint) {
34            return end_ptr;
35        }
36    }
37    return nullptr;
38}
39
40static uint8_t* strrchr_4(uint8_t* start_ptr, uint8_t* end_ptr, char32_t codepoint)
41{
42    while (start_ptr < end_ptr) {
43        end_ptr -= 4;
44        char32_t c = *(char32_t*) end_ptr;
45        if (c == codepoint) {
46            return end_ptr;
47        }
48    }
49    return nullptr;
50}
51
52StrRChr _pw_strrchr_variants[5] = {
53    nullptr,
54    strrchr_1,
55    strrchr_2,
56    strrchr_3,
57    strrchr_4
58};
59
60[[nodiscard]] bool pw_strrchr(PwValuePtr str, char32_t chr, unsigned end_pos, unsigned* result)
61{
62    pw_assert(pw_is_string(str));
63    uint8_t char_size = str->str_params.char_size;
64    if (_pw_unlikely(char_size < calc_char_size(chr))) {
65        return false;
66    }
67    unsigned length;
68    uint8_t* start_ptr = _pw_string_start_length(str, &length);
69
70    if (_pw_unlikely(length == 0)) {
71        return false;
72    }
73    if (end_pos > length) {
74        end_pos = length;
75    }
76    uint8_t* end_ptr = start_ptr + end_pos * char_size;
77    StrRChr fn_strrchr = _pw_strrchr_variants[char_size];
78    uint8_t* char_ptr = fn_strrchr(start_ptr, end_ptr, chr);
79    if (_pw_unlikely(!char_ptr)) {
80        return false;
81    }
82    if (result) {
83        *result = (char_ptr - start_ptr) / char_size;
84    }
85    return true;
86}