feat(sv): add string view lib
This commit is contained in:
@@ -5,11 +5,15 @@ project(
|
|||||||
DESCRIPTION "C Libraries for recreational Programming."
|
DESCRIPTION "C Libraries for recreational Programming."
|
||||||
LANGUAGES C)
|
LANGUAGES C)
|
||||||
|
|
||||||
add_library(c-libs ${CMAKE_CURRENT_SOURCE_DIR}/src/dyn-arr.c)
|
add_library(c-libs ${CMAKE_CURRENT_SOURCE_DIR}/src/dyn-arr.c
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/src/string-view.c)
|
||||||
target_include_directories(
|
target_include_directories(
|
||||||
c-libs PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
c-libs PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
||||||
$<INSTALL_INTERFACE:include>)
|
$<INSTALL_INTERFACE:include>)
|
||||||
|
|
||||||
|
add_executable(strings ${CMAKE_CURRENT_SOURCE_DIR}/app/strings.c)
|
||||||
|
target_link_libraries(strings PRIVATE c-libs)
|
||||||
|
|
||||||
include(CTest)
|
include(CTest)
|
||||||
if(BUILD_TESTING)
|
if(BUILD_TESTING)
|
||||||
find_package(PkgConfig REQUIRED)
|
find_package(PkgConfig REQUIRED)
|
||||||
|
|||||||
49
app/strings.c
Normal file
49
app/strings.c
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
#include "c-libs/dyn-arr.h"
|
||||||
|
#include "c-libs/string-view.h"
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
StringView sv = sv_new("Hello World\n");
|
||||||
|
|
||||||
|
sv_puts(sv);
|
||||||
|
sv_puts(sv_drop(sv_shrink(sv, 3), 2));
|
||||||
|
sv_puts(sv_drop(sv_shrink(sv, 5), 9));
|
||||||
|
|
||||||
|
StringView sv2 = sv_new("AAA Test BBB");
|
||||||
|
sv_puts(sv2);
|
||||||
|
puts("");
|
||||||
|
sv2 = sv_seek(sv2, ' ');
|
||||||
|
sv_puts(sv2);
|
||||||
|
puts("");
|
||||||
|
sv2 = sv_seek_back(sv2, ' ');
|
||||||
|
sv_puts(sv2);
|
||||||
|
puts("");
|
||||||
|
|
||||||
|
StringView csv = sv_new(" 100 , 2000, 10 ,1,871");
|
||||||
|
|
||||||
|
StringView *values = NULL;
|
||||||
|
darr_init(values, 0);
|
||||||
|
|
||||||
|
while (sv_len(csv)) {
|
||||||
|
StringView item = sv_split_at(&csv, ',');
|
||||||
|
darr_push(values, sv_trim(item, isspace));
|
||||||
|
}
|
||||||
|
|
||||||
|
char *all = sv_concat_with_sep(values, darr_size(values), sv_new("::"));
|
||||||
|
puts(all);
|
||||||
|
|
||||||
|
darr_clear(values);
|
||||||
|
|
||||||
|
StringView ccsv = sv_new(all);
|
||||||
|
while (sv_len(ccsv)) {
|
||||||
|
StringView item = sv_split_at_sv(&ccsv, sv_new("::"));
|
||||||
|
darr_push(values, sv_trim(item, isspace));
|
||||||
|
}
|
||||||
|
|
||||||
|
char *all2 = sv_concat_with_sep(values, darr_size(values), sv_new("-+-"));
|
||||||
|
puts(all2);
|
||||||
|
|
||||||
|
free(all);
|
||||||
|
free(all2);
|
||||||
|
darr_free(values);
|
||||||
|
}
|
||||||
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
|
||||||
2
src/string-view.c
Normal file
2
src/string-view.c
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#define CLIBS_STRING_VIEW_IMPL
|
||||||
|
#include "c-libs/string-view.h"
|
||||||
Reference in New Issue
Block a user