ohmu
·
2026-10-15
1#pragma once
2
3#include <stdbool.h>
4#include <stdint.h>
5#include <string.h>
6#include <ctype.h>
7
8#ifdef _WIN32
9// #include <shlwapi.h>
10__stdcall char* StrStrIA(const char* lpFirst, const char* lpSrch);
11 #define strcasestr StrStrIA
12#endif
13
14#define FF_STR_INDIR(x) #x
15#define FF_STR(x) FF_STR_INDIR(x)
16
17// Everything below is a leaf predicate over the bytes it is given: it reads memory through its
18// arguments and nothing else, so `gnu::pure` is accurate. `gnu::const` is deliberately not used --
19// these are already `always_inline`, so it would buy no optimization, and it would become a lie the
20// day one of them is rewritten around a lookup table.
21//
22// The `gnu::nonnull` indexes name the arguments that are dereferenced unconditionally. `ffStrSet`
23// and `ffStrCopy` are not listed: both handle a null pointer explicitly.
24
25[[nodiscard, gnu::pure]]
26static inline bool ffStrSet(const char* str) {
27 if (str == nullptr) {
28 return false;
29 }
30
31 while (isspace(*str)) {
32 str++;
33 }
34
35 return *str != '\0';
36}
37
38[[gnu::always_inline, gnu::nonnull(1, 2), gnu::pure, nodiscard]]
39static inline bool ffStrStartsWithIgnCase(const char* str, const char* compareTo) {
40 return strncasecmp(str, compareTo, strlen(compareTo)) == 0;
41}
42
43[[gnu::always_inline, gnu::nonnull(1, 2), gnu::pure, nodiscard]]
44static inline bool ffStrEqualsIgnCase(const char* str, const char* compareTo) {
45 return strcasecmp(str, compareTo) == 0;
46}
47
48[[gnu::always_inline, gnu::nonnull(1, 2), gnu::pure, nodiscard]]
49static inline bool ffStrStartsWith(const char* str, const char* compareTo) {
50 return strncmp(str, compareTo, strlen(compareTo)) == 0;
51}
52
53[[gnu::nonnull(1, 2), gnu::pure, nodiscard]]
54static inline bool ffStrEndsWith(const char* str, const char* compareTo) {
55 size_t strLength = strlen(str);
56 size_t compareToLength = strlen(compareTo);
57 if (strLength < compareToLength) {
58 return false;
59 }
60 return memcmp(str + strLength - compareToLength, compareTo, compareToLength) == 0;
61}
62
63[[gnu::nonnull(1, 2), gnu::pure, nodiscard]]
64static inline bool ffStrEndsWithIgnCase(const char* str, const char* compareTo) {
65 size_t strLength = strlen(str);
66 size_t compareToLength = strlen(compareTo);
67 if (strLength < compareToLength) {
68 return false;
69 }
70 return strncasecmp(str + strLength - compareToLength, compareTo, compareToLength) == 0;
71}
72
73[[gnu::always_inline, gnu::nonnull(1, 2), gnu::pure, nodiscard]]
74static inline bool ffStrEquals(const char* str, const char* compareTo) {
75 return strcmp(str, compareTo) == 0;
76}
77
78[[gnu::always_inline, gnu::nonnull(1, 2), gnu::pure, nodiscard]]
79static inline bool ffStrContains(const char* str, const char* compareTo) {
80 return strstr(str, compareTo) != nullptr;
81}
82
83[[gnu::always_inline, gnu::nonnull(1, 2), gnu::pure, nodiscard]]
84static inline bool ffStrContainsIgnCase(const char* str, const char* compareTo) {
85 return strcasestr(str, compareTo) != nullptr;
86}
87
88[[gnu::always_inline, gnu::nonnull(1), gnu::pure, nodiscard]]
89static inline bool ffStrContainsC(const char* str, char compareTo) {
90 return strchr(str, compareTo) != nullptr;
91}
92
93[[gnu::always_inline, gnu::pure, nodiscard]]
94static inline bool ffCharIsEnglishAlphabet(char c) {
95 return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z');
96}
97
98[[gnu::always_inline, gnu::pure, nodiscard]]
99static inline bool ffCharIsDigit(char c) {
100 return '0' <= c && c <= '9';
101}
102
103// Parse one UTF-8 character, returning consumed byte count and display width.
104// Invalid / incomplete sequence falls back to one-byte width=1.
105// If the Unicode codepoint is non-printable, width becomes 0.
106// Not `pure`: it stores the width through `width` when that is not null.
107[[gnu::nonnull(1)]]
108uint8_t ffUtf8CharLenWidth(const char* str, uint32_t length, uint8_t* width);
109
110[[gnu::nonnull(1), gnu::pure, nodiscard]]
111uint32_t ffUtf8StrWidth(const char* str, uint32_t length);
112
113[[gnu::always_inline, gnu::pure, nodiscard]]
114static inline bool ffCharIsHexDigit(char c) {
115 return ffCharIsDigit(c) || ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F');
116}
117
118[[gnu::always_inline, gnu::pure, nodiscard]]
119static inline int8_t ffHexCharToInt(char c) {
120 if (ffCharIsDigit(c)) {
121 return (int8_t) (c - '0');
122 } else if ('a' <= c && c <= 'f') {
123 return (int8_t) (c - 'a' + 10);
124 } else if ('A' <= c && c <= 'F') {
125 return (int8_t) (c - 'A' + 10);
126 } else {
127 return -1;
128 }
129}
130
131// Copies at most (dstBufSiz - 1) bytes from src to dst; dst is always null-terminated
132// Returns a pointer to the end of the copy, which callers are free to ignore, so not `nodiscard`.
133// Not `pure`: it writes through `dst`. `dst` may be null, `src` may not.
134[[gnu::nonnull(2)]]
135static inline char* ffStrCopy(char* __restrict__ dst, const char* __restrict__ src, size_t dstBufSiz) {
136 if (__builtin_expect(dst == nullptr, false) || dstBufSiz == 0) {
137 return dst;
138 }
139
140 size_t len = strnlen(src, dstBufSiz - 1);
141 memcpy(dst, src, len);
142 dst[len] = '\0';
143 return dst + len;
144}