summaryrefslogtreecommitdiff
path: root/include/al/str.h
blob: 20a1c26753518d4a6043a686b2f74d800d417dd2 (plain)
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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
#ifndef _AL_STR_H
#define _AL_STR_H

#include "lib.h"

typedef struct {
    u32 length;
    u32 alloc;
    char *__sized_by(alloc) data;
} str;

#define AL_STR_NO_POS ((u32)-1)

#define al_str_null() ((str){ 0, 0, NULL })

#define al_str_at(s, index) (s)->data[index]
#define al_str_atr(s, index) (s)->data[(s)->length - (index)]

#define al_str_c(c) ((str){ (u32)(sizeof(c) - 1), 0, AL_CHARS_IF_LITERAL(c) })
#define al_str_cs(c) ((str){ (u32)al_strnlen(c, sizeof(c)), 0, ((char *)(c)) }) // (s)ized
#define al_str_cr(c) ((str){ (u32)al_strnlen(c, MAX_ALLOC_32), 0, ((char *)(c)) }) // (r)untime

#define al_str_w(c, index, n) ((str){ ((u32)(n)), 0, (c) + (index) })
#define al_str_substr(s, start, end) al_str_w((s)->data, start, ((end) - (start)))

#define al_str_x(s) (s)->length, (s)->data // e(x)pand
#define al_str_x_maxlen(s, len) MIN((s)->length, (len)), (s)->data

static inline bool al_str_is_empty(str *s)
{
    return s->length == 0;
}

static inline void al_str_free(str *s)
{
    if (s->alloc) al_free(s->data);
}

static inline u32 al_str_reserve(str *s, u32 size)
{
    return al_growing_allocation((void **)&s->data, s->alloc, size, al_realloc);
}

static inline void al_str_sized(str *s, u32 size)
{
    *s = al_str_null();
    s->alloc = al_str_reserve(s, size);
}

static inline void al_str_clone(str *dest, str *src)
{
    al_str_sized(dest, src->length);
    al_memcpy(dest->data, src->data, src->length);
    dest->length = src->length;
}

static inline void al_str_from(str *s, const char *c_str)
{
    size_t length = al_strlen(c_str);
    al_str_sized(s, (u32)length);
    al_memcpy(s->data, c_str, length);
    s->length = (u32)length;
}

static inline void al_str_to_lower(str *s)
{
    for (u32 i = 0; i < s->length; i++) {
        al_str_at(s, i) = al_tolower(al_str_at(s, i));
    }
}

static inline void al_str_to_upper(str *s)
{
    for (u32 i = 0; i < s->length; i++) {
        al_str_at(s, i) = al_toupper(al_str_at(s, i));
    }
}

// Returned c_str must be free'd by the user.
static inline char *al_str_to_c_str(str *s)
{
    char *c_str = (char *)al_malloc(s->length + 1);
    al_memcpy(c_str, s->data, s->length);
    c_str[s->length] = '\0';
    return c_str;
}

static inline void al_str_cat(str *s, str *c)
{
    s->alloc = al_str_reserve(s, s->length + c->length);
    al_memcpy(s->data + s->length, c->data, c->length);
    s->length += c->length;
}

static inline void al_str_insert(str *s, u32 index, str *c)
{
    s->alloc = al_str_reserve(s, s->length + c->length);
    char *pos = s->data + index;
    if (index < s->length) {
        al_memmove(pos + c->length, pos, s->length - index);
    }
    al_memcpy(pos, c->data, c->length);
    s->length += c->length;
}

static inline void al_str_prepend(str *s, str *c)
{
    al_str_insert(s, 0, c);
}

static inline void al_str_remove_at(str *s, u32 index)
{
    al_assert(index < s->length);
    if (index != --s->length) {
        al_memmove(s->data + index, s->data + index + 1, s->length - index);
    }
}

static inline void al_str_replace(str *s, char c, char n)
{
    for (u32 i = 0; i < s->length; i++) {
        if (al_str_at(s, i) == c) {
            al_str_at(s, i) = n;
        }
    }
}

static inline s32 al_str_cmp(str *s, str *c, u32 start, u32 length)
{
    if (length > c->length) return 1;
    else if (length + start > s->length) return -1;
    return al_memcmp(&al_str_at(s, start), c->data, length);
}

static inline bool al_str_eq(str *s, str *c)
{
    return al_str_cmp(s, c, 0, MAX(s->length, c->length)) == 0;
}

static inline u32 al_str_find(str *s, char c)
{
    char *loc = (char *)al_memchr(s->data, c, s->length);
    return loc ? (u32)(loc - s->data) : AL_STR_NO_POS;
}

static inline u32 al_str_rfind(str *s, char c)
{
    char *loc = (char *)al_memrchr(s->data, c, s->length);
    return loc ? (u32)(loc - s->data) : AL_STR_NO_POS;
}

// extremely slow.
static inline u32 al_str_find_str(str *s, str *c)
{
    if (c->length <= s->length) {
        for (u32 i = 0; i < s->length - c->length; i++) {
            if (al_str_at(s, i) == al_str_at(c, 0) &&
                (al_str_cmp(s, c, i, c->length) == 0)) {
                return i;
            }
        }
    }
    return AL_STR_NO_POS;
}

static inline bool al_str_tok(str *result, char delim)
{
    u32 index = al_str_find(result, delim);
    if (index == AL_STR_NO_POS) {
        return false;
    }
    *result = al_str_substr(result, index + 1, result->length);
    return true;
}

// djb2 hash.
static inline u32 al_str_hash(str *s)
{
    u32 hash = 5381;
    for (u32 i = 0; i < s->length; i++) {
        // hash * 33 + c
        hash = ((hash << 5) + hash) + (uchar)al_str_at(s, i);
    }
    return hash;
}

s64 al_str_to_long(str *s, u32 base, bool *error);
bool al_str_get_line(str *buffer, char sep, str *line);

#endif // _AL_STR_H