Skip to content

Commit 9995af4

Browse files
committed
add new functions to sv
1 parent 32ef0ba commit 9995af4

4 files changed

Lines changed: 447 additions & 1 deletion

File tree

README.md

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,26 @@ gcc main.c -o app -I/usr/local/include/strlib -L/usr/local/lib -lstrlib
191191

192192
## Reference
193193

194+
### SVArray
195+
196+
```c
197+
typedef struct {
198+
SV *data;
199+
size_t len;
200+
size_t cap;
201+
} SVArray;
202+
```
203+
204+
A heap-allocated growable array of SVs. Returned by `sv_split`. Always call `sva_free` when done.
205+
206+
`SVArray sva_new(void)` — Creates an empty array. Does not allocate until first push.
207+
208+
`void sva_push(SVArray *arr, SV sv)` — Appends an SV to the array. Grows automatically.
209+
210+
`SV sva_get(SVArray *arr, size_t i)` — Returns the SV at index i. Aborts with an error message if i is out of bounds.
211+
212+
`void sva_free(SVArray *arr)` — Frees the backing array and zeroes the struct. Safe to call on an already-freed `SVArray`. Does not free the individual SVs — they are views and do not own memory.
213+
194214
### SV
195215

196216
**Functions:**
@@ -221,12 +241,64 @@ gcc main.c -o app -I/usr/local/include/strlib -L/usr/local/lib -lstrlib
221241

222242
`SV sv_chop_by_delim(SV *sv, char delim)` — Returns everything before the first `delim` and advances `sv` past it. If not found, returns the full view and leaves `sv` empty.
223243

244+
`bool sv_is_empty(SV sv)` — Returns true if sv has zero length.
245+
246+
`bool sv_is_whitespace(SV sv)` — Returns true if every byte is whitespace. Returns false on empty.
247+
248+
`bool sv_is_alpha(SV sv)` — Returns true if every byte is an alphabetic character. Returns false on empty.
249+
250+
`bool sv_is_numeric(SV sv)` — Returns true if every byte is a decimal digit. Returns false on empty.
251+
252+
`bool sv_is_alphanumeric(SV sv)` — Returns true if every byte is alphabetic or a decimal digit. Returns false on empty.
253+
254+
`bool sv_is_upper(SV sv)` — Returns true if every byte is an uppercase letter. Returns false on empty.
255+
256+
`bool sv_is_lower(SV sv)` — Returns true if every byte is a lowercase letter. Returns false on empty.
257+
258+
`size_t sv_count_sv(SV sv, SV needle, bool overlapping)` — Counts occurrences of needle in sv. If overlapping is false, matches do not overlap — `sv_count("aaa", "aa", false)` returns 1. If overlapping is true, every position is checked — `sv_count("aaa", "aa", true)` returns 2. Returns 0 if needle is empty or longer than sv.
259+
260+
`bool sv_parse_int(SV sv, int *out)` — Parses sv as a decimal integer into `*out`. Returns true on success, false if the input is not a valid integer or overflows the type. If out is NULL, validates without storing. All sv_parse_* variants follow this same contract.
261+
262+
`bool sv_parse_long(SV sv, long *out)` — Parses as `long`.
263+
264+
`bool sv_parse_longlong(SV sv, long long *out)` — Parses as `long long`.
265+
266+
`bool sv_parse_int8(SV sv, int8_t *out)` — Parses as `int8_t`.
267+
268+
`bool sv_parse_int16(SV sv, int16_t *out)` — Parses as `int16_t`.
269+
270+
`bool sv_parse_int32(SV sv, int32_t *out)` — Parses as `int32_t`.
271+
272+
`bool sv_parse_int64(SV sv, int64_t *out)` — Parses as `int64_t`.
273+
274+
`bool sv_parse_uint8(SV sv, uint8_t *out)` — Parses as `uint8_t`.
275+
276+
`bool sv_parse_uint16(SV sv, uint16_t *out)` — Parses as `uint16_t`.
277+
278+
`bool sv_parse_uint32(SV sv, uint32_t *out)` — Parses as `uint32_t`.
279+
280+
`bool sv_parse_uint64(SV sv, uint64_t *out)` — Parses as `uint64_t`.
281+
282+
`bool sv_parse_float(SV sv, float *out)` — Parses as `float`.
283+
284+
`bool sv_parse_double(SV sv, double *out)` — Parses as `double`.
285+
286+
`SVArray sv_split_char(SV sv, char delim, size_t maxsplit)` — Splits sv on every occurrence of delim. If maxsplit is non-zero, stops after that many splits and puts the remainder in the final element. Empty segments are preserved. Caller must call `sva_free` on the result.
287+
288+
`SVArray sv_split_sv(SV sv, SV delim, size_t maxsplit)` — Same as `sv_split_char` but splits on a multi-character delimiter. If delim is empty, returns sv unsplit as a single element.
289+
224290
**Macros:**
225291

226292
`NEW_SV(s)` — Converts `SV`, `char *`, or `const char *` into an `SV` at compile time via `_Generic`. No copy.
227293

228294
`sv_eq(a, b)`, `sv_cmp(a, b)`, `sv_starts_with(a, b)`, `sv_ends_with(a, b)`, `sv_contains(a, b)`, `sv_find(a, b)` — Type-coercing wrappers around their `_sv` counterparts. Accept any mix of `SV`, `char *`, or `const char *` as arguments.
229295

296+
`sv_count(a, b)` — Counts non-overlapping occurrences of b in a. Accepts any mix of SV, `char *`, or `const char *`.
297+
298+
`sv_count_overlapping(a, b)` — Counts overlapping occurrences of b in a. Accepts any mix of SV, `char *`, or `const char *`.
299+
300+
`sv_split(sv, delim, maxsplit)` — Splits sv on delim, dispatching to `sv_split_char` or `sv_split_sv` based on the type of delim at compile time. Accepts char, SV, `char *`, or `const char *` as delimiter. Pass 0 for maxsplit to split everything.
301+
230302
`SV_FMT`, `SV_ARGS(sv)` — Use with printf-style functions to print an SV without null-termination:
231303
```c
232304
printf(SV_FMT "\n", SV_ARGS(my_sv));
@@ -354,6 +426,16 @@ str_free(&a);
354426
str_free(&b);
355427
```
356428
429+
### `SVArray` views into freed memory
430+
431+
The SVs inside an `SVArray` returned by `sv_split` are views into the original string's memory. Freeing or modifying the source string while still using the array will leave all the views dangling:
432+
```c
433+
SVArray parts = sv_split(sv_from_cstr(get_temp_string()), ',', 0); // imagine get_temp_string() is a fn that returns temp string
434+
// if get_temp_string()'s memory is gone, parts.data[i] are all dangling
435+
sva_free(&parts);
436+
```
437+
Make sure the source string outlives the `SVArray`.
438+
357439
### `SB_AUTO` / `STR_AUTO` are no-ops on unknown compilers
358440

359441
On compilers without `__attribute__((cleanup))`, both macros expand to nothing and no warning is emitted at the point of use — only at the point `compat.h` is processed. Variables marked `SB_AUTO` or `STR_AUTO` will not be freed automatically. You will leak memory silently.

include/compat.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,9 @@
4242
/* ── Feature flags ── */
4343
#if defined(STRLIB_COMPILER_GCC) || defined(STRLIB_COMPILER_CLANG)
4444
#define STRLIB_HAS_CLEANUP
45-
#define STRLIB_HAS_TYPEOF
45+
#define STRLIB_TYPEOF(x) __typeof__(x)
46+
#else
47+
#define STRLIB_TYPEOF(x)
4648
#endif
4749

4850
/* _Generic is C11, already guaranteed by standard check above */

include/sv.h

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,26 @@
55

66
#include <stddef.h>
77
#include <stdbool.h>
8+
#include <stdint.h>
89

910
typedef struct
1011
{
1112
const char *data;
1213
size_t len;
1314
} SV;
1415

16+
typedef struct
17+
{
18+
SV *data;
19+
size_t len;
20+
size_t cap;
21+
} SVArray;
22+
23+
SVArray sva_new(void);
24+
void sva_push(SVArray *arr, SV sv);
25+
SV sva_get(SVArray *arr, size_t i);
26+
void sva_free(SVArray *arr);
27+
1528
#define NEW_SV(s) _Generic((s), \
1629
SV: (s), \
1730
char *: sv_from_cstr(s), \
@@ -35,6 +48,31 @@ SV sv_trim(SV sv);
3548

3649
SV sv_chop_by_delim(SV *sv, char delim);
3750

51+
bool sv_is_empty(SV sv);
52+
bool sv_is_whitespace(SV sv);
53+
bool sv_is_alpha(SV sv);
54+
bool sv_is_numeric(SV sv);
55+
bool sv_is_alphanumeric(SV sv);
56+
bool sv_is_upper(SV sv);
57+
bool sv_is_lower(SV sv);
58+
59+
size_t sv_count_sv(SV sv, SV needle, bool overlapping);
60+
61+
bool sv_parse_int(SV sv, int *out);
62+
bool sv_parse_long(SV sv, long *out);
63+
bool sv_parse_longlong(SV sv, long long *out);
64+
bool sv_parse_int8(SV sv, int8_t *out);
65+
bool sv_parse_int16(SV sv, int16_t *out);
66+
bool sv_parse_int32(SV sv, int32_t *out);
67+
bool sv_parse_int64(SV sv, int64_t *out);
68+
bool sv_parse_uint8(SV sv, uint8_t *out);
69+
bool sv_parse_uint16(SV sv, uint16_t *out);
70+
bool sv_parse_uint32(SV sv, uint32_t *out);
71+
bool sv_parse_uint64(SV sv, uint64_t *out);
72+
73+
SVArray sv_split_char(SV sv, char delim, size_t maxsplit);
74+
SVArray sv_split_sv(SV sv, SV delim, size_t maxsplit);
75+
3876
#define sv_eq(a, b) sv_eq_sv(NEW_SV(a), NEW_SV(b))
3977
#define sv_cmp(a, b) sv_cmp_sv(NEW_SV(a), NEW_SV(b))
4078

@@ -43,6 +81,15 @@ SV sv_chop_by_delim(SV *sv, char delim);
4381
#define sv_contains(a, b) sv_contains_sv(NEW_SV(a), NEW_SV(b))
4482
#define sv_find(a, b) sv_find_sv(NEW_SV(a), NEW_SV(b))
4583

84+
#define sv_count(a, b) sv_count_sv(NEW_SV(a), NEW_SV(b), false)
85+
#define sv_count_overlapping(a, b) sv_count_sv(NEW_SV(a), NEW_SV(b), true)
86+
87+
#define sv_split(sv, delim, maxsplit) _Generic((delim), \
88+
char: sv_split_char((sv), (delim), (maxsplit)), \
89+
SV: sv_split_sv((sv), (delim), (maxsplit)), \
90+
char *: sv_split_sv((sv), NEW_SV(delim), (maxsplit)), \
91+
const char *: sv_split_sv((sv), NEW_SV(delim), (maxsplit)))
92+
4693
#define SV_FMT "%.*s"
4794
#define SV_ARGS(sv) (int)(sv).len, (sv).data
4895

0 commit comments

Comments
 (0)