MULE 0.0.1
Multi-Use Library Essentials
Loading...
Searching...
No Matches
Str and StrView

Overview

Owned, growable byte strings, and borrowed views into them.

Two types do all the work. A Str owns a heap buffer and can grow. A StrView borrows bytes that something else owns and never allocates. The working pattern is to put text into a Str once, then pass StrViews around to search it, split it and cut it. Copy a view into a new Str only when the bytes must outlive their owner.

Str s = { 0 };
CHECK(str_from_cstr(&s, "foo"));
CHECK(str_append(&s, SV("bar")));
CHECK(str_push(&s, '!'));
CHECK_EQ_STR(str_cstr(&s), "foobar!");

Str

  • data is NUL-terminated whenever it is non-null, so str_cstr() can hand it to C APIs. len is still the authority, and embedded NULs are allowed.
  • capacity counts content bytes. The allocation is capacity + 1 bytes, to hold the terminator.
  • A zero-initialised Str (Str s = { 0 };) is a valid empty string that owns no memory. Every function accepts it.
  • The first allocation holds 15 bytes, and each growth doubles the capacity, so appending one byte at a time costs O(1) amortised.
  • alloc says where the bytes live, and null means the libc heap. Set it before the first allocation and leave it alone after. str_free() keeps it.
  • Pass a Str to functions as Str *. Str b = a; copies the pointer, not the bytes, and freeing both is a double free.
Arena arena = { 0 };
Str s = { .alloc = arena_allocator(&arena) };
CHECK(str_append_cstr(&s, "lives in the arena"));
CHECK(str_appendf(&s, ", %d bytes", 18));
CHECK_EQ_STR(str_cstr(&s), "lives in the arena, 18 bytes");
arena_free(&arena); /* frees the string's bytes too */

StrView

  • A view is a pointer and a length. Pass it by value.
  • It is valid while its owner is alive and has not moved its buffer. Any call that may grow a Str may move it, and every view into it then dangles.
  • It is not NUL-terminated in general. Print it with %.*s and STR_FMT, never with s.
  • No StrView function allocates, so none of them can fail. Make a view with SV(), sv_from_cstr(), str_view() or sv_slice().

Errors

A function that may allocate returns bool. False means out of memory, a size that would overflow, or a read error for the file functions. A call that returns false leaves the Str exactly as it was, so && can chain calls and stops at the first failure.

The tutorial Str and StrView in practice walks through all of this with programs you can run, and shows what the common mistakes look like under AddressSanitizer.

Data Structures

struct  Str
 Owned, growable byte string. More...
struct  StrView
 Non-owning view of bytes; handy for passing slices and literals around. More...

Macros

#define SV(lit)
 View of a string literal, with its length computed at compile time.
#define STR_FMT(s)
 printf helper: printf("%.*s", STR_FMT(s)) prints exactly len bytes.

Lifetime

Initialise a Str with contents, and free it.

bool str_with_capacity (Str *s, size_t capacity)
 Initialise s as empty with room for capacity bytes of content.
bool str_from (Str *s, StrView v)
 Initialise s as a copy of v.
bool str_from_cstr (Str *s, const char *cstr)
 Initialise s as a copy of cstr.
void str_free (Str *s)
 Free the buffer and reset s to the zero state.

Capacity

Control allocation when you know more than the doubling policy does.

bool str_reserve (Str *s, size_t additional)
 Ensure additional more bytes fit without another allocation.
bool str_shrink_to_fit (Str *s)
 Shrink the allocation to exactly len + 1 bytes.
void str_clear (Str *s)
 Set the length to zero.

Appending

Add bytes to the end.

Each call may move data.

bool str_push (Str *s, char c)
 Append the single byte c.
bool str_append (Str *s, StrView v)
 Append v.
bool str_append_cstr (Str *s, const char *cstr)
 Append cstr.
bool str_appendf (Str *s, const char *fmt,...)
 Append the printf-style expansion of fmt.
bool str_vappendf (Str *s, const char *fmt, va_list ap)
 str_appendf() for a va_list, like vprintf.

Reading files

Fill a Str from a FILE *.

bool str_append_file (Str *s, FILE *f)
 Append everything left to read from f.
bool str_read_line (Str *s, FILE *f)
 Replace the contents of s with the next line of f, without its \n.

Access and comparison

Look at a Str, or turn a C string into a view.

StrView str_view (const Str *s)
 Borrow the whole of s.
const char * str_cstr (const Str *s)
 NUL-terminated contents of s, for C APIs such as fopen and puts.
bool str_eq (StrView a, StrView b)
 True when a and b hold the same bytes.
StrView sv_from_cstr (const char *cstr)
 View of cstr, measured with strlen.

Searching and slicing

Inspect a view and cut pieces out of it.

Nothing here allocates.

size_t sv_find_byte (StrView v, char c)
 Index of the first c in v.
size_t sv_find (StrView v, StrView needle)
 Index of the first needle in v.
int sv_cmp (StrView a, StrView b)
 Byte order like memcmp.
bool sv_starts_with (StrView v, StrView prefix)
 True when v begins with prefix.
bool sv_ends_with (StrView v, StrView suffix)
 True when v ends with suffix.
StrView sv_slice (StrView v, size_t from, size_t to)
 Bytes from up to but not including to.
StrView sv_trim (StrView v)
 v without leading and trailing ASCII whitespace: space, \t, \n, \v, \f and \r.
#define SV_NPOS   SIZE_MAX
 Returned by the find functions when there is no match.

Splitting

Walk a view piece by piece.

Both functions cut the next piece off the front of rest, and return false when nothing is left.

bool sv_split_next (StrView *rest, StrView sep, StrView *piece)
 Cut the piece before sep off rest.
bool sv_token_next (StrView *rest, StrView delims, StrView *token)
 strtok without the mutation: skip delims, then cut the next run of other bytes.

Parsing

Turn a whole view into a number.

Partial matches fail.

bool sv_parse_i64 (StrView v, int64_t *out)
 Parse all of v as a base-10 integer with an optional sign.
bool sv_parse_f64 (StrView v, double *out)
 Parse all of v as a double, in any form strtod accepts in the C locale.

Data Structure Documentation

◆ Str

struct Str

Owned, growable byte string.

Zero-initialise before first use.

Examples
01_build.c, 02_reuse.c, 03_file.c, 07_lines.c, 08_keep.c, 09_replace.c, 10_dangling.c, 11_double_free.c, and main.c.

Definition at line 64 of file str.h.

Data Fields
char * data Buffer, or null when nothing is allocated.
size_t len Bytes of content, excluding the terminator.
size_t capacity Content bytes that fit before the next realloc.
const Allocator * alloc Source of data.

Null means the heap.

◆ StrView

struct StrView

Non-owning view of bytes; handy for passing slices and literals around.

Examples
04_views.c, 05_split.c, 06_sort.c, 07_lines.c, 08_keep.c, 09_replace.c, 10_dangling.c, and main.c.

Definition at line 72 of file str.h.

Data Fields
const char * data First byte.

Not necessarily NUL-terminated.

size_t len Number of bytes in the view.

Macro Definition Documentation

◆ SV

#define SV ( lit)
Value:
((StrView){ .data = "" lit "", .len = sizeof(lit) - 1 })
Non-owning view of bytes; handy for passing slices and literals around.
Definition str.h:72

View of a string literal, with its length computed at compile time.

Only a literal compiles, so a char * cannot silently get the length of a pointer. Use sv_from_cstr() for pointers.

Examples
01_build.c, 04_views.c, 05_split.c, 06_sort.c, 07_lines.c, 08_keep.c, 09_replace.c, and main.c.

Definition at line 80 of file str.h.

◆ STR_FMT

#define STR_FMT ( s)
Value:
(int)(s).len, (s).data

printf helper: printf("%.*s", STR_FMT(s)) prints exactly len bytes.

Works on a Str and on a StrView.

Example
Str s = { 0 };
CHECK(str_from_cstr(&s, "hello"));
char buf[32];
snprintf(buf, sizeof buf, "[%.*s]", STR_FMT(s));
CHECK_EQ_STR(buf, "[hello]");
Examples
01_build.c, 04_views.c, 05_split.c, 06_sort.c, 07_lines.c, 09_replace.c, and 10_dangling.c.

Definition at line 85 of file str.h.

◆ SV_NPOS

#define SV_NPOS   SIZE_MAX

Returned by the find functions when there is no match.

Examples
04_views.c, 07_lines.c, and 09_replace.c.

Definition at line 217 of file str.h.

Function Documentation

◆ str_with_capacity()

bool str_with_capacity ( Str * s,
size_t capacity )

Initialise s as empty with room for capacity bytes of content.

Parameters
[out]sOverwritten without being freed. Only alloc is kept.
capacityContent bytes to make room for.
Returns
False when out of memory.
Warning
It does not free what s held. Call str_free() first on a Str that owns memory.

Definition at line 36 of file str.c.

◆ str_from()

bool str_from ( Str * s,
StrView v )

Initialise s as a copy of v.

Parameters
[out]sOverwritten without being freed. Only alloc is kept.
vThe bytes to copy.
Returns
False when out of memory.
Warning
It does not free what s held. Call str_free() first on a Str that owns memory.

Definition at line 41 of file str.c.

◆ str_from_cstr()

bool str_from_cstr ( Str * s,
const char * cstr )

Initialise s as a copy of cstr.

Null gives an empty string.

Returns
False when out of memory.
See also
str_from()

Definition at line 46 of file str.c.

◆ str_free()

void str_free ( Str * s)

Free the buffer and reset s to the zero state.

Keeps alloc, so the string can be reused. Safe to call twice.

Example
Str s = { 0 };
CHECK(str_from_cstr(&s, "temporary"));
CHECK(str_from_cstr(&s, "reused"));

Definition at line 50 of file str.c.

◆ str_reserve()

bool str_reserve ( Str * s,
size_t additional )

Ensure additional more bytes fit without another allocation.

Reserve before taking views, and appends within the reserve will not move them.

Returns
False when out of memory or len + additional overflows.

Definition at line 56 of file str.c.

◆ str_shrink_to_fit()

bool str_shrink_to_fit ( Str * s)

Shrink the allocation to exactly len + 1 bytes.

Returns
False when the allocator fails. s is then unchanged.

Definition at line 62 of file str.c.

◆ str_clear()

void str_clear ( Str * s)

Set the length to zero.

Keeps the allocation for reuse, so a loop that builds one string per pass allocates once.

Example
Str s = { 0 };
CHECK(str_append_cstr(&s, "data"));
CHECK(s.capacity >= 100);

Definition at line 71 of file str.c.

◆ str_push()

bool str_push ( Str * s,
char c )

Append the single byte c.

Returns
False when out of memory. s is then unchanged.

Definition at line 76 of file str.c.

◆ str_append()

bool str_append ( Str * s,
StrView v )

Append v.

v may point into s itself.

Returns
False when out of memory. s is then unchanged.
Example
Str s = { 0 };
CHECK(str_from_cstr(&s, "abc"));
CHECK_EQ_STR(str_cstr(&s), "abcabc");

Definition at line 89 of file str.c.

◆ str_append_cstr()

bool str_append_cstr ( Str * s,
const char * cstr )

Append cstr.

A null cstr appends nothing.

Returns
False when out of memory. s is then unchanged.

Definition at line 119 of file str.c.

◆ str_appendf()

bool str_appendf ( Str * s,
const char * fmt,
... )

Append the printf-style expansion of fmt.

The compiler checks the arguments against the format.

Returns
False when out of memory or the format fails. s is then unchanged.
Example
Str s = { 0 };
CHECK(str_appendf(&s, "%d-%s", 42, "x"));
CHECK(str_appendf(&s, "|%05.1f", 3.14159));
CHECK_EQ_STR(str_cstr(&s), "42-x|003.1");

Definition at line 123 of file str.c.

◆ str_vappendf()

bool str_vappendf ( Str * s,
const char * fmt,
va_list ap )

str_appendf() for a va_list, like vprintf.

Parameters
sThe string to append to.
fmtA printf format for the arguments in ap.
apUsed up afterwards. va_copy it first to use it again.
Returns
False when out of memory or the format fails.

Definition at line 131 of file str.c.

◆ str_append_file()

bool str_append_file ( Str * s,
FILE * f )

Append everything left to read from f.

When s owns no buffer yet, it takes the read buffer over instead of copying it.

Returns
False on a read error or out of memory. s is then unchanged. Check ferror(f) before fclose to tell the two apart.
Example
FILE *f = tmpfile();
REQUIRE(f != NULL);
fputs("line one\nline two\n", f);
rewind(f);
Str s = { 0 };
CHECK_EQ_STR(str_cstr(&s), "line one\nline two\n");
fclose(f);

Definition at line 156 of file str.c.

◆ str_read_line()

bool str_read_line ( Str * s,
FILE * f )

Replace the contents of s with the next line of f, without its \n.

A \r before it is kept, and embedded NULs are allowed.

Returns
False at end of input, on a read error (check ferror(f)) or out of memory. The contents of s are then unchanged, though its capacity may have grown.
Example
FILE *f = tmpfile();
REQUIRE(f != NULL);
fputs("first\nsecond\n", f);
rewind(f);
Str line = { 0 };
size_t lines = 0, bytes = 0;
while (str_read_line(&line, f)) {
lines++;
bytes += line.len; /* "first", then "second": no newlines */
}
CHECK_EQ_INT(lines, 2);
CHECK_EQ_INT(bytes, 11);
str_free(&line);
fclose(f);

Definition at line 185 of file str.c.

◆ str_view()

StrView str_view ( const Str * s)

Borrow the whole of s.

Warning
Invalidated by any call that may reallocate s.

Definition at line 212 of file str.c.

◆ str_cstr()

const char * str_cstr ( const Str * s)

NUL-terminated contents of s, for C APIs such as fopen and puts.

Never null, even for an empty Str.

Warning
Invalidated by any call that may reallocate s.

Definition at line 216 of file str.c.

◆ str_eq()

bool str_eq ( StrView a,
StrView b )

True when a and b hold the same bytes.

Example
CHECK(str_eq(SV("abc"), sv_from_cstr("abc")));
CHECK(!str_eq(SV("abc"), SV("abd")));
CHECK(!str_eq(SV("abc"), SV("ab")));
See also
sv_cmp() to order strings.

Definition at line 220 of file str.c.

◆ sv_from_cstr()

StrView sv_from_cstr ( const char * cstr)

View of cstr, measured with strlen.

Null gives an empty view. Use it for argv[i], getenv and other C strings.

Definition at line 224 of file str.c.

◆ sv_find_byte()

size_t sv_find_byte ( StrView v,
char c )

Index of the first c in v.

Returns
The index, or SV_NPOS when c does not occur.

Definition at line 231 of file str.c.

◆ sv_find()

size_t sv_find ( StrView v,
StrView needle )

Index of the first needle in v.

An empty needle is at 0.

Returns
The index, or SV_NPOS when needle does not occur.
Example
StrView v = SV("key=value=more");
size_t at = sv_find(v, SV("="));
CHECK_EQ_INT(at, 3);
CHECK_SV(sv_slice(v, 0, at), "key");
CHECK_SV(sv_slice(v, at + 1, v.len), "value=more");
CHECK(sv_find(v, SV("missing")) == SV_NPOS);

Definition at line 237 of file str.c.

◆ sv_cmp()

int sv_cmp ( StrView a,
StrView b )

Byte order like memcmp.

A prefix sorts before the longer string, so it works as a qsort comparison on views.

Returns
Negative, zero or positive as a sorts before, with or after b.

Definition at line 252 of file str.c.

◆ sv_starts_with()

bool sv_starts_with ( StrView v,
StrView prefix )

True when v begins with prefix.

Definition at line 259 of file str.c.

◆ sv_ends_with()

bool sv_ends_with ( StrView v,
StrView suffix )

True when v ends with suffix.

Definition at line 263 of file str.c.

◆ sv_slice()

StrView sv_slice ( StrView v,
size_t from,
size_t to )

Bytes from up to but not including to.

Precondition
from <= to && to <= v.len. An assert checks it.

Definition at line 268 of file str.c.

◆ sv_trim()

StrView sv_trim ( StrView v)

v without leading and trailing ASCII whitespace: space, \t, \n, \v, \f and \r.

Definition at line 279 of file str.c.

◆ sv_split_next()

bool sv_split_next ( StrView * rest,
StrView sep,
StrView * piece )

Cut the piece before sep off rest.

Empty pieces are kept, so a,,b, splits as a, empty, b. Use it for fields and lines.

Parameters
[in,out]restThe text still to split. It shrinks on every call.
sepThe separator. It may be several bytes, such as "\r\n".
[out]pieceThe next piece. Unchanged when the call returns false.
Returns
False when rest is empty.
Precondition
sep is not empty.
Example
StrView rest = SV("a,,b,"), piece;
Str got = { 0 };
while (sv_split_next(&rest, SV(","), &piece)) {
CHECK(str_appendf(&got, "[%.*s]", STR_FMT(piece)));
}
CHECK_EQ_STR(str_cstr(&got), "[a][][b]");
str_free(&got);
See also
sv_token_next() to skip empty pieces.

Definition at line 286 of file str.c.

◆ sv_token_next()

bool sv_token_next ( StrView * rest,
StrView delims,
StrView * token )

strtok without the mutation: skip delims, then cut the next run of other bytes.

Empty pieces never appear. Use it for words.

Parameters
[in,out]restThe text still to split. It shrinks on every call.
delimsA set of single bytes, any of which separates tokens.
[out]tokenThe next token. Unchanged when the call returns false.
Returns
False when only delimiters are left.
Example
StrView rest = SV(" the quick,, brown "), word;
Str got = { 0 };
while (sv_token_next(&rest, SV(" ,"), &word)) {
CHECK(str_appendf(&got, "[%.*s]", STR_FMT(word)));
}
CHECK_EQ_STR(str_cstr(&got), "[the][quick][brown]");
str_free(&got);
See also
sv_split_next() to keep empty pieces.

Definition at line 306 of file str.c.

◆ sv_parse_i64()

bool sv_parse_i64 ( StrView v,
int64_t * out )

Parse all of v as a base-10 integer with an optional sign.

Parameters
vThe whole text to parse.
[out]outThe value. Unchanged when the call returns false.
Returns
False on whitespace, trailing bytes or overflow.
Example
/* scanf("%" SCNd64 " %lf") without scanf: split, then parse each part. */
StrView rest = SV("42 2.5"), word;
int64_t count = 0;
double scale = 0;
CHECK(sv_token_next(&rest, SV(" "), &word) && sv_parse_i64(word, &count));
CHECK(sv_token_next(&rest, SV(" "), &word) && sv_parse_f64(word, &scale));
CHECK_EQ_INT(count, 42);
CHECK(scale == 2.5);

Definition at line 330 of file str.c.

◆ sv_parse_f64()

bool sv_parse_f64 ( StrView v,
double * out )

Parse all of v as a double, in any form strtod accepts in the C locale.

Parameters
vThe whole text to parse.
[out]outThe value. Unchanged when the call returns false.
Returns
False on whitespace, trailing bytes, overflow to infinity, or a view over 255 bytes.

Definition at line 351 of file str.c.