feat(sv): add string view lib
This commit is contained in:
350
include/c-libs/string-view.h
Normal file
350
include/c-libs/string-view.h
Normal file
@@ -0,0 +1,350 @@
|
||||
#ifndef CLIBS_STRING_VIEW_H
|
||||
#define CLIBS_STRING_VIEW_H
|
||||
|
||||
#include <malloc.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
/// A string view consisting of a start pointer _data_
|
||||
/// and an end pointer _end_. The end points behind the last character.
|
||||
typedef struct {
|
||||
const char *data;
|
||||
const char *end;
|
||||
} StringView;
|
||||
|
||||
///
|
||||
///@brief Get the length of a string view
|
||||
///
|
||||
///@param StringView sv the string view
|
||||
///
|
||||
///@return size_t it's length (data - end)
|
||||
///
|
||||
size_t sv_len(StringView sv);
|
||||
|
||||
///
|
||||
///@brief Construct a new string view with a data and length specification
|
||||
///
|
||||
///@param const char *str the string to view (can be non-null-terminated)
|
||||
///@param size_t len the length of the string
|
||||
///
|
||||
///@return StringView the new string view
|
||||
///
|
||||
StringView sv_new_sized(const char *str, size_t len);
|
||||
|
||||
///
|
||||
///@brief Construct a new string view from a null-terminated string
|
||||
///
|
||||
///@param const char *str the null-terminated string
|
||||
///
|
||||
///@return StringView a new string view {.data = str, .end = str + strlen(str)}
|
||||
///
|
||||
StringView sv_new(const char *str);
|
||||
|
||||
///
|
||||
///@brief Drop the first n chars of a string view (non-destructive)
|
||||
///
|
||||
///@param StringView sv the original sv
|
||||
///@param size_t n the number of chars to drop from the beginning
|
||||
///
|
||||
///@return StringView the resulting sv
|
||||
///
|
||||
StringView sv_drop(StringView sv, size_t n);
|
||||
|
||||
///
|
||||
///@brief Get the first n chars of a string view (non-destructive)
|
||||
///
|
||||
///@param StringView sv the original sv
|
||||
///@param size_t n the number of chars to take
|
||||
///
|
||||
///@return StringView the resulting sv
|
||||
///
|
||||
StringView sv_take(StringView sv, size_t n);
|
||||
|
||||
///
|
||||
///@brief Remove the n last chars of a sv (non-destructive)
|
||||
///
|
||||
///@param StringView sv the original sv
|
||||
///@param size_t n the number of chars to remove from the end
|
||||
///
|
||||
///@return StringView the resulting sv
|
||||
///
|
||||
StringView sv_shrink(StringView sv, size_t n);
|
||||
|
||||
///
|
||||
///@brief call _putc_ for each char in the string view
|
||||
///
|
||||
///@param StringView sv the string view to print
|
||||
///
|
||||
void sv_puts(StringView sv);
|
||||
|
||||
///
|
||||
///@brief Remove chars from the front until a specified char is encountered
|
||||
///(non-destructive)
|
||||
///
|
||||
///@param StringView sv the original sv
|
||||
///@param char c the char to find
|
||||
///
|
||||
///@return StringView the resulting string view (may be empty)
|
||||
///
|
||||
StringView sv_seek(StringView sv, char c);
|
||||
|
||||
///
|
||||
///@brief Like sv_seek but seeking from end to begin
|
||||
///
|
||||
///@param StringView sv the original sv
|
||||
///@param char c the char to find
|
||||
///
|
||||
///@return StringView the resulting string view
|
||||
///
|
||||
StringView sv_seek_back(StringView sv, char c);
|
||||
|
||||
///
|
||||
///@brief Remove chars from the front matching pred (non-destructive)
|
||||
///
|
||||
///@param StringView sv the original sv
|
||||
///@param int (*pred)(int) the predicate (returns non-zero if char shall be
|
||||
/// removed)
|
||||
///
|
||||
///@return StringView the resulting string view
|
||||
///
|
||||
StringView sv_trim_front(StringView sv, int (*pred)(int));
|
||||
|
||||
///
|
||||
///@brief Like sv_trim_front but from end to begin
|
||||
///
|
||||
///@param StringView sv the original sv
|
||||
///@param int (*pred)(int) the predicate (returns non-zero if char shall be
|
||||
///
|
||||
///@return StringView the resulting string view
|
||||
///
|
||||
StringView sv_trim_back(StringView sv, int (*pred)(int));
|
||||
|
||||
///
|
||||
///@brief Effectively sv_trim_front and sv_trim_back
|
||||
///
|
||||
///@param StringView sv the original sv
|
||||
///@param int (*pred)(int) the predicate (returns non-zero if char shall be
|
||||
///
|
||||
///@return StringView the resulting string view
|
||||
///
|
||||
StringView sv_trim(StringView sv, int (*pred)(int));
|
||||
|
||||
///
|
||||
///@brief Split the string view at the first occurance of c. (destructive, sv
|
||||
/// will be the "second half" after the split)
|
||||
///
|
||||
///@param StringView *sv the string view to split at. Will be the part after the
|
||||
/// first _c_ if no c found this is empty.
|
||||
///@param char c the character to split at
|
||||
///
|
||||
///@return StringView the part before _c_, of no _c_ found this will be equal to
|
||||
/// sv
|
||||
///
|
||||
StringView sv_split_at(StringView *sv, char c);
|
||||
|
||||
///
|
||||
///@brief Check of two string_views have equal contents
|
||||
///
|
||||
///@param StringView a
|
||||
///@param StringView b
|
||||
///
|
||||
///@return int non-zero if a is equal to b
|
||||
///
|
||||
int sv_eq(StringView a, StringView b);
|
||||
|
||||
///
|
||||
///@brief Like sv_split_at but using sep as a separator (destructive)
|
||||
///
|
||||
///@param StringView *sv the string to split. Will contain the second half (can
|
||||
/// be empty if no sep found)
|
||||
///@param StringView sep
|
||||
///
|
||||
///@return StringView will be the first half (can be equal to sv, if no sep
|
||||
/// found)
|
||||
///
|
||||
StringView sv_split_at_sv(StringView *sv, StringView sep);
|
||||
|
||||
///
|
||||
///@brief Create a null-terminated string from the sv, by copying the contents.
|
||||
/// Must be freed
|
||||
///
|
||||
///@return char the new c-string, must be freed
|
||||
///
|
||||
char *sv_clone(StringView sv);
|
||||
|
||||
///
|
||||
///@brief Concatenate a list of string views with a separator. The result must
|
||||
///be freed
|
||||
///
|
||||
///@return char a null-terminated string containing all svs separated with sep.
|
||||
///Must be freed
|
||||
///
|
||||
char *sv_concat_with_sep(const StringView *svs, size_t n, StringView sep);
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef CLIBS_STRING_VIEW_IMPL
|
||||
|
||||
size_t sv_len(StringView sv) { return sv.end - sv.data; }
|
||||
|
||||
StringView sv_new_sized(const char *str, size_t len) {
|
||||
return (StringView){
|
||||
.data = str,
|
||||
.end = str + len,
|
||||
};
|
||||
}
|
||||
|
||||
StringView sv_new(const char *str) { return sv_new_sized(str, strlen(str)); }
|
||||
|
||||
StringView sv_drop(StringView sv, size_t n) {
|
||||
return (StringView){
|
||||
.data = sv.data + n,
|
||||
.end = sv.end,
|
||||
};
|
||||
}
|
||||
|
||||
StringView sv_take(StringView sv, size_t n) {
|
||||
return (StringView){
|
||||
.data = sv.data,
|
||||
.end = sv.data + n < sv.end ? sv.data + n : sv.end,
|
||||
};
|
||||
}
|
||||
|
||||
StringView sv_shrink(StringView sv, size_t n) {
|
||||
return (StringView){
|
||||
.data = sv.data,
|
||||
.end = sv.end - n,
|
||||
};
|
||||
}
|
||||
|
||||
void sv_puts(StringView sv) {
|
||||
while (sv.data < sv.end) {
|
||||
putc(*sv.data++, stdout);
|
||||
}
|
||||
}
|
||||
|
||||
StringView sv_seek(StringView sv, char c) {
|
||||
while (sv.data < sv.end && *sv.data != c) {
|
||||
sv.data++;
|
||||
}
|
||||
|
||||
return sv;
|
||||
}
|
||||
|
||||
StringView sv_seek_back(StringView sv, char c) {
|
||||
while (sv.data < sv.end && *(sv.end - 1) != c) {
|
||||
sv.end--;
|
||||
}
|
||||
|
||||
return sv;
|
||||
}
|
||||
|
||||
StringView sv_trim_front(StringView sv, int (*pred)(int)) {
|
||||
while (sv.data < sv.end && pred(*sv.data)) {
|
||||
sv.data++;
|
||||
}
|
||||
|
||||
return sv;
|
||||
}
|
||||
|
||||
StringView sv_trim_back(StringView sv, int (*pred)(int)) {
|
||||
while (sv.data < sv.end && pred(*(sv.end - 1))) {
|
||||
sv.end--;
|
||||
}
|
||||
|
||||
return sv;
|
||||
}
|
||||
|
||||
StringView sv_trim(StringView sv, int (*pred)(int)) {
|
||||
return sv_trim_front(sv_trim_back(sv, pred), pred);
|
||||
}
|
||||
|
||||
StringView sv_split_at(StringView *sv, char c) {
|
||||
StringView current = *sv;
|
||||
|
||||
while (sv->data < sv->end && *sv->data != c) {
|
||||
sv->data++;
|
||||
}
|
||||
|
||||
// Remove sep, current is the first part, sv the second
|
||||
if (sv->data < sv->end) {
|
||||
current.end = sv->data++;
|
||||
}
|
||||
|
||||
return current;
|
||||
}
|
||||
|
||||
int sv_eq(StringView a, StringView b) {
|
||||
if (sv_len(a) != sv_len(b)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (a.data < a.end) {
|
||||
if (*a.data != *b.data) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
a.data++;
|
||||
b.data++;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
StringView sv_split_at_sv(StringView *sv, StringView sep) {
|
||||
StringView current = *sv;
|
||||
size_t sep_len = sv_len(sep);
|
||||
|
||||
while (sv->data < sv->end && !sv_eq(sv_take(*sv, sep_len), sep)) {
|
||||
sv->data++;
|
||||
}
|
||||
|
||||
// Remove sep, current is the first part, sv the second
|
||||
if (sv_len(*sv) >= sep_len) {
|
||||
current.end = sv->data;
|
||||
sv->data += sep_len;
|
||||
} else {
|
||||
sv->data = sv->end;
|
||||
}
|
||||
|
||||
return current;
|
||||
}
|
||||
|
||||
char *sv_clone(StringView sv) {
|
||||
size_t n = sv_len(sv) + 1;
|
||||
char *data = malloc(n);
|
||||
|
||||
data[n - 1] = 0;
|
||||
memcpy(data, sv.data, n - 1);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
char *sv_concat_with_sep(const StringView *svs, size_t n, StringView sep) {
|
||||
size_t len = 1; // Reserve nullterminator
|
||||
size_t sep_len = sv_len(sep);
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
len += sv_len(svs[i]);
|
||||
}
|
||||
len += sep_len * (n - 1);
|
||||
|
||||
char *data = malloc(len);
|
||||
char *write_head = data;
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (i > 0) {
|
||||
memcpy(write_head, sep.data, sep_len);
|
||||
write_head += sep_len;
|
||||
}
|
||||
|
||||
size_t l = sv_len(svs[i]);
|
||||
memcpy(wri
|
||||
write_head += l;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user