summaryrefslogtreecommitdiffstats
path: root/src/common/strutil.h
blob: 00a8a083984231f438513af13ff498e6e1719594 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
#pragma once

#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <ctype.h>

#include "common/wcwidth.h"

#ifdef _WIN32
// #include <shlwapi.h>
__stdcall char* StrStrIA(const char* lpFirst, const char* lpSrch);
    #define strcasestr StrStrIA
#endif

static inline bool ffStrSet(const char* str) {
    if (str == NULL) {
        return false;
    }

    while (isspace(*str)) {
        str++;
    }

    return *str != '\0';
}

static inline bool ffStrStartsWithIgnCase(const char* str, const char* compareTo) {
    return strncasecmp(str, compareTo, strlen(compareTo)) == 0;
}

static inline bool ffStrEqualsIgnCase(const char* str, const char* compareTo) {
    return strcasecmp(str, compareTo) == 0;
}

static inline bool ffStrStartsWith(const char* str, const char* compareTo) {
    return strncmp(str, compareTo, strlen(compareTo)) == 0;
}

static inline bool ffStrEndsWith(const char* str, const char* compareTo) {
    size_t strLength = strlen(str);
    size_t compareToLength = strlen(compareTo);
    if (strLength < compareToLength) {
        return false;
    }
    return memcmp(str + strLength - compareToLength, compareTo, compareToLength) == 0;
}

static inline bool ffStrEndsWithIgnCase(const char* str, const char* compareTo) {
    size_t strLength = strlen(str);
    size_t compareToLength = strlen(compareTo);
    if (strLength < compareToLength) {
        return false;
    }
    return strncasecmp(str + strLength - compareToLength, compareTo, compareToLength) == 0;
}

static inline bool ffStrEquals(const char* str, const char* compareTo) {
    return strcmp(str, compareTo) == 0;
}

static inline bool ffStrContains(const char* str, const char* compareTo) {
    return strstr(str, compareTo) != NULL;
}

static inline bool ffStrContainsIgnCase(const char* str, const char* compareTo) {
    return strcasestr(str, compareTo) != NULL;
}

static inline bool ffStrContainsC(const char* str, char compareTo) {
    return strchr(str, compareTo) != NULL;
}

static inline bool ffCharIsEnglishAlphabet(char c) {
    return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z');
}

static inline bool ffCharIsDigit(char c) {
    return '0' <= c && c <= '9';
}

// Parse one UTF-8 character, returning consumed byte count and display width.
// Invalid / incomplete sequence falls back to one-byte width=1.
// If the Unicode codepoint is non-printable, width becomes 0.
uint8_t ffUtf8CharLenWidth(const char* str, uint32_t length, uint8_t* width);

uint32_t ffUtf8StrWidth(const char* str, uint32_t length);

static inline bool ffCharIsHexDigit(char c) {
    return ffCharIsDigit(c) || ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F');
}

static inline int8_t ffHexCharToInt(char c) {
    if (ffCharIsDigit(c)) {
        return (int8_t) (c - '0');
    } else if ('a' <= c && c <= 'f') {
        return (int8_t) (c - 'a' + 10);
    } else if ('A' <= c && c <= 'F') {
        return (int8_t) (c - 'A' + 10);
    } else {
        return -1;
    }
}

// Copies at most (dstBufSiz - 1) bytes from src to dst; dst is always null-terminated
static inline char* ffStrCopy(char* __restrict__ dst, const char* __restrict__ src, size_t dstBufSiz) {
    if (__builtin_expect(dst == NULL, false) || dstBufSiz == 0) {
        return dst;
    }

    size_t len = strnlen(src, dstBufSiz - 1);
    memcpy(dst, src, len);
    dst[len] = '\0';
    return dst + len;
}