-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathzdx_string_view.h
358 lines (286 loc) · 9.46 KB
/
zdx_string_view.h
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
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
/**
* MIT License
*
* Copyright (c) 2024 Mudit Ameta
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
/**
* THIS LIBRARY SHOULD NEVER ALLOCATE!
* It should only provide readonly views of the underlying buffer.
* Also, it should use only automatic storage so no pointers
* should be passed in to any functions unless the function needs to
* "return" multiple values by side effecting the incoming
* string_view value - for e.g., sv_split_by_char that returns the
* first split chunk and also updates the incoming sv_t value to point
* after the chunk that was split out.
*
* THIS LIBRARY SHOULD NEVER DEPEND ON ANY OTHER EXCEPT STANDARD LIBS!
* IT SHOULD BE FULLY SELF-SUFFICIENT TO BE ATOMIC!
* Serving these properties lets us rely on this library as a
* building block library.
*
* Keep it simple!
*/
#ifndef ZDX_STRING_VIEW_H_
#define ZDX_STRING_VIEW_H_
#pragma GCC diagnostic error "-Wsign-conversion"
#ifndef SV_API
#define SV_API
#endif // SV_API
#ifndef SV_ASSERT
#define SV_ASSERT assertm
#endif // SV_ASSERT
#include <stddef.h>
#include <stdbool.h>
typedef struct string_view {
const char *buf;
size_t length;
} sv_t;
#define SV_FMT "%.*s"
#define sv_fmt_args(sv) (int)(sv).length, (sv).buf
SV_API sv_t sv_from_buf(const char buf[const static 1], const size_t len);
SV_API sv_t sv_from_cstr(const char str[const static 1]);
SV_API bool sv_begins_with_word_buf(sv_t sv, const char buf[const static 1], const size_t len);
SV_API bool sv_begins_with_word_cstr(sv_t sv, const char str[const static 1]);
SV_API bool sv_eq_cstr(sv_t sv, const char str[const static 1]);
SV_API bool sv_eq_sv(const sv_t sv1, const sv_t sv2);
SV_API bool sv_is_empty(const sv_t sv);
SV_API bool sv_has_char(const sv_t sv1, const char c);
SV_API sv_t sv_trim_left(sv_t sv);
SV_API sv_t sv_trim_right(sv_t sv);
SV_API sv_t sv_trim(sv_t sv);
SV_API sv_t sv_split_by_char(sv_t sv[const static 1], char delim);
SV_API sv_t sv_split_from_idx(sv_t sv[const static 1], const size_t from); /* including index (from) */
SV_API sv_t sv_split_until_idx(sv_t sv[const static 1], const size_t until); /* excluding index (until) */
// ----------------------------------------------------------------------------------------------------------------
#ifdef ZDX_STRING_VIEW_IMPLEMENTATION
#include <ctype.h>
#include <string.h>
#include "./zdx_util.h"
#define sv_dbg(label, sv) dbg("%s buf \""SV_FMT"\" (%p) \t| length %zu", \
(label), sv_fmt_args(sv), (void *)(sv).buf, (sv).length)
#define sv_assert_validity(sv) { \
SV_ASSERT((sv).buf != NULL, "Expected: non-NULL buf in string view, Received: %p", (void *)(sv).buf); \
SV_ASSERT((sv).length >= 0, "Expected: positive length string view, Received: %zu", (sv).length); \
} while(0); /* <- this semicolon is to silence the empty while loop body when SV_ASSERT is not defined */
SV_API sv_t sv_from_buf(const char buf[const static 1], const size_t len)
{
SV_ASSERT(buf != NULL, "Expected: input buffer to be not-NULL, Received: %p", (void *)buf);
dbg(">> buf \"%.*s\" (buf = %p, len = %zu)", (int)len, buf, (void *)buf, len);
sv_t sv = {
.buf = buf,
.length = len
};
sv_dbg("<<", sv);
return sv;
}
SV_API sv_t sv_from_cstr(const char str[const static 1])
{
SV_ASSERT(str != NULL, "Expected: input cstr to be not-NULL, Received: %p", (void *)str);
dbg(">> cstr \"%s\"", str);
sv_t sv = {
.buf = str,
.length = strlen(str)
};
sv_dbg("<<", sv);
return sv;
}
SV_API bool sv_begins_with_word_buf(sv_t sv, const char buf[const static 1], const size_t len)
{
sv_dbg("<<", sv);
sv_assert_validity(sv);
SV_ASSERT(buf != NULL, "Expected: input buffer to be not-NULL, Received: %p", (void *)buf);
if (len > sv.length) {
return false;
} else if (len == sv.length) {
return memcmp(sv.buf, buf, len) == 0;
} else {
return memcmp(sv.buf, buf, len) == 0 && isspace(sv.buf[len]);
}
return false;
}
SV_API bool sv_begins_with_word_cstr(sv_t sv, const char str[const static 1])
{
SV_ASSERT(str != NULL, "Expected: input cstr to be not-NULL, Received: %p", (void *)str);
dbg(">> cstr \"%s\"", str);
return sv_begins_with_word_buf(sv, str, strlen(str));
}
SV_API bool sv_eq_cstr(sv_t sv, const char str[const static 1])
{
sv_dbg(">>", sv);
sv_assert_validity(sv);
SV_ASSERT(str != NULL, "Expected: input string to be not-NULL, Received: %p", (void *)str);
dbg(">> match with %s", str);
const size_t input_str_len = strlen(str);
// empty strings
if ((input_str_len + sv.length) == 0) {
dbg("<< true");
return true;
}
if (input_str_len == sv.length && *sv.buf == *str && memcmp(sv.buf, str, sv.length) == 0) {
dbg("<< true");
return true;
}
dbg("<< false");
return false;
}
SV_API bool sv_eq_sv(const sv_t sv1, const sv_t sv2)
{
sv_dbg(">> sv1", sv1);
sv_dbg(">> sv2", sv1);
// empty strings
if ((sv1.length + sv2.length) == 0) {
return true;
}
if (sv1.length == sv2.length && *sv1.buf == *sv2.buf && memcmp(sv1.buf, sv2.buf, sv1.length) == 0) {
return true;
}
return false;
}
SV_API bool sv_has_char(const sv_t sv, const char c)
{
for (size_t i = 0; i < sv.length; i++) {
if (sv.buf[i] == c) {
return true;
}
}
return false;
}
SV_API bool sv_is_empty(const sv_t sv)
{
return sv.buf == NULL && sv.length == 0;
}
SV_API sv_t sv_trim_left(sv_t sv)
{
sv_dbg(">>", sv);
sv_assert_validity(sv);
for (size_t i = 0; i < sv.length; i++) {
if (!isspace(sv.buf[i])) {
sv_t new_sv = { .buf = (sv.buf + i), .length = (sv.length - i) };
sv_dbg("<<", new_sv);
return new_sv;
}
}
return sv;
sv_dbg("<<", sv);
}
SV_API sv_t sv_trim_right(sv_t sv)
{
sv_dbg(">>", sv);
sv_assert_validity(sv);
if (sv.length > 0) {
for (size_t i = (sv.length - 1); i >= 0; i--) {
if (!isspace(sv.buf[i])) {
sv_t new_sv = { .buf = sv.buf, .length = i + 1 };
sv_dbg("<<", new_sv);
return new_sv;
}
}
}
sv_dbg("<<", sv);
return sv;
}
SV_API sv_t sv_trim(sv_t sv)
{
return sv_trim_right(sv_trim_left(sv));
}
/**
* Example usage:
* sv_t sv = sv_from_cstr("abc..123...000");
* sv_t isv = {0};
* do {
* isv = sv_split_by_char(&sv, '.');
* printf("ret: "SV_FMT" (%zu) remaining: "SV_FMT" (%zu)\n", sv_fmt_args(isv), isv.length, sv_fmt_args(sv), sv.length);
* } while(isv.buf);
*
* Output: abc -> "" -> 123 -> "" -> "" -> 000 -> tombstone (which is (sv_t){ .buf = NULL, .length = 0 })
*/
SV_API sv_t sv_split_by_char(sv_t sv[const static 1], char delim)
{
sv_dbg(">>", *sv);
dbg(">> delimiter '%c'", delim);
sv_assert_validity(*sv);
sv_t split = {0};
// sv being split is empty. Return this tombstone where split.buf = NULL and split.length = 0
if (sv->length <= 0) {
return split;
}
split = (sv_t){ .buf = sv->buf, .length = 0 };
bool found_delim = false;
for (size_t i = 0; i < sv->length; i++) {
split.length++;
if (sv->buf[i] == delim) {
found_delim = true;
break;
}
}
sv->buf = sv->buf + split.length;
sv->length = sv->length - split.length;
if (found_delim) {
split.length--;
}
sv_dbg("<<", split);
return split;
}
SV_API sv_t sv_split_from_idx(sv_t sv[const static 1], const size_t from) /* including index (from) */
{
sv_dbg(">>", *sv);
dbg(">> split from index %zu", from);
sv_assert_validity(*sv);
sv_t split = { .buf = sv->buf, .length = 0 };
if (sv->length != 0) {
const size_t offset = from > sv->length ? sv->length : from;
const size_t len = from > sv->length ? 0 : (sv->length - from);
sv_t split = {
.buf = sv->buf + offset,
.length = len,
};
sv->length = offset;
sv_dbg("<< updated input", *sv);
sv_dbg("<< split chunk", split);
return split;
}
sv_dbg("<<", split);
return split;
}
SV_API sv_t sv_split_until_idx(sv_t sv[const static 1], const size_t until) /* excluding index (until) */
{
sv_dbg(">>", *sv);
dbg(">> split until index %zu", until);
sv_assert_validity(*sv);
sv_t split = { .buf = sv->buf, .length = 0 };
if (sv->length != 0) {
const size_t len = until > sv->length ? sv->length : until;
sv_t split = {
.buf = sv->buf,
.length = len
};
sv->buf = sv->buf + len;
sv->length = sv->length - len;
sv_dbg("<< updated input", *sv);
sv_dbg("<< split chunk", split);
return split;
}
sv_dbg("<<", split);
return split;
}
#endif // ZDX_STRING_VIEW_IMPLEMENTATION
#endif // ZDX_STRING_VIEW_H_