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}