From ccc45a1dc249160364aa12b3cfecc9844d6760ff Mon Sep 17 00:00:00 2001 From: Tyge Løvset Date: Tue, 18 May 2021 10:08:35 +0200 Subject: Added new file: csview: String View --- README.md | 1 + docs/csview_api.md | 155 ++++++++++++++++++++++++++++++++++++++++++++++++++++ examples/svmap.c | 38 +++++++++++++ stc/csview.h | 157 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 351 insertions(+) create mode 100644 docs/csview_api.md create mode 100644 examples/svmap.c create mode 100644 stc/csview.h diff --git a/README.md b/README.md index a316bb1c..02cf93b9 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,7 @@ the multi-map/set variants: - [***csset*** - **std::set** sorted set alike type](docs/csset_api.md) - [***cstack*** - **std::stack** alike (adapter) type](docs/cstack_api.md) - [***cstr*** - **std::string** alike type](docs/cstr_api.md) +- [***csview*** - **std::string_view** alike type](docs/csview_api.md) - [***cvec*** - **std::vector** alike type](docs/cvec_api.md) Others: diff --git a/docs/csview_api.md b/docs/csview_api.md new file mode 100644 index 00000000..4098114c --- /dev/null +++ b/docs/csview_api.md @@ -0,0 +1,155 @@ +# STC [csview](../stc/csview.h): String View +![String](pics/string.jpg) + +The type **csview** is a string view and can refer to a constant contiguous sequence of char-elements with the first +element of the sequence at position zero. The implementation holds two members: a pointer to constant char and a size. + +**csview** is an efficient replacent for `const char*`. It never allocates memory, and therefore need not be destructed. +Its lifetime is limited by the source string storage. It keeps the length of the string, and does not call *strlen()* +when passing it around. It is faster when using`csview` as convertion type (raw) than `const char*` in associative +containers with cstr keys. E.g. prefer `using_cmap_svkey()` over `using_cmap_strkey()`. + +See the c++ class [std::basic_string_view](https://en.cppreference.com/w/cpp/string/basic_string_view) for a functional +description. + +## Header file + +All csview definitions and prototypes are available by including a single header file. + +```c +#include +``` +## Methods + +```c +csview csview_new(const char literal_only[]); // csview from literal, no strlen() +csview c_sv(const char literal_only[]); // shorthand for csview_new() +csview csview_from(const char* str); // construct +csview csview_from_n(const char* str, size_t n); // construct +csview csview_from_s(cstr s); // construct +csview csview_substr(csview sv, size_t pos, size_t n); + +size_t csview_size(csview sv); +size_t csview_length(csview sv); +bool csview_empty(csview sv); + +void csview_clear(csview* self); + +bool csview_equals(csview sv, csview sv2); +size_t csview_find(csview sv, csview needle); +bool csview_contains(csview sv, csview needle); +bool csview_begins_with(csview sv, csview sub); +bool csview_ends_with(csview sv, csview sub); + +const char* csview_front(const csview* self); +const char* csview_back(const csview* self); + +csview_iter_t csview_begin(const csview* self); +csview_iter_t csview_end(const csview* self); +void csview_next(csview_iter_t* it); +``` +#### Extended cstr methods +```c +cstr cstr_from_v(csview sv); +csview cstr_to_v(const cstr* self); +cstr* cstr_assign_v(cstr* self, csview sv); +cstr* cstr_append_v(cstr* self, csview sv); +void cstr_insert_v(cstr* self, size_t pos, csview sv); +void cstr_replace_v(cstr* self, size_t pos, size_t len, csview sv); + +bool cstr_equals_v(cstr s, csview sv); +size_t cstr_find_v(cstr s, csview needle); +bool cstr_contains_v(cstr s, csview needle); +bool cstr_begins_with_v(cstr s, csview sub); +bool cstr_ends_with_v(cstr s, csview sub); +``` +#### Helper methods +```c +int csview_compare_ref(const csview* x, const csview* y); +bool csview_equals_ref(const csview* x, const csview* y); +uint64_t csview_hash_ref(const csview* x, size_t ignored); +``` +## Types + +| Type name | Type definition | Used to represent... | +|:------------------|:------------------------------------------|:-------------------------| +| `csview` | `struct { const char *str; size_t size }` | The string view type | +| `csview_value_t` | `char` | The string element type | +| `csview_iter_t` | `struct { csview_value_t *ref; }` | csview iterator | + +## Constants and macros + +| Name | Value | +|:------------------|:------------------| +| `csview_null` | csview null value | + +## Container adaptors +``` +using_cvec_sv() +using_cdeq_sv() +using_clist_sv() + +using_csmap_svkey(X, Mapped) +using_csmap_svkey(X, Mapped, mappedDel) +using_csmap_svkey(X, Mapped, mappedDel, mappedClone) +using_csmap_svkey(X, Mapped, mappedDel, mappedFromRaw, mappedToRaw, RawMapped) +using_csmap_sv() +using_csset_sv() + +using_cmap_svkey(X, Mapped) +using_cmap_svkey(X, Mapped, mappedDel) +using_cmap_svkey(X, Mapped, mappedDel, mappedClone) +using_cmap_svkey(X, Mapped, mappedDel, mappedFromRaw, mappedToRaw, RawMapped) +using_cmap_sv() +using_cset_sv() +``` + +## Example +```c +#include + +using_cmap_svkey(si, int); // cmap with csview as convertion type +using_cvec_sv(); // cvec with csview as convertion type + +int main() +{ + csview text = c_sv("A long and winded literal string"); + printf("%s\nLength: %zu\n\n", text.str, text.size); + + c_var (cvec_sv, vec, { + c_sv("Element 1"), + c_sv("Element 2"), + c_sv("Element 3") + }); + cvec_sv_push_back(&vec, cstr_new("Second last element")); + cvec_sv_emplace_back(&vec, c_sv("Last element")); // converts from csview to cstr + + c_foreach (i, cvec_sv, vec) + printf("%s\n", i.ref->str); + + c_var (cmap_si, map, { + {c_sv("hello"), 100}, + {c_sv("world"), 200} + }); + cmap_si_emplace(&map, c_sv("gone mad"), 300); + + cmap_si_value_t* v = cmap_si_get(&map, c_sv("world")); + printf("\n%s: %d\n", v->first.str, v->second); + + cmap_si_del(&map); + cvec_sv_del(&vec); +} +``` +Output: +``` +A long and winded literal string +Length: 32 + +Great +Fantastic +Sensational +Second last element +Last element + +world: 200 +``` diff --git a/examples/svmap.c b/examples/svmap.c new file mode 100644 index 00000000..f4bcc311 --- /dev/null +++ b/examples/svmap.c @@ -0,0 +1,38 @@ +#include +#include +#include + +// cmap with csview as convertion key-type (raw): +using_cmap_svkey(si, int); + +int main() { + csview fox = c_sv("The quick brown fox jumps over the lazy dog."); + printf("\"%s\", length=%zu\n", fox.str, fox.size); + + cmap_si frequencies = cmap_si_init(); + + // Non-emplace: cstr element API + cmap_si_insert(&frequencies, cstr_new("thousand"), 1000); + cmap_si_insert_or_assign(&frequencies, cstr_new("thousand"), 2000); // update; same as put() + cmap_si_insert(&frequencies, cstr_new("thousand"), 3000); // ignored + + // Emplace: csview element API + const char* key = "hundred"; + cmap_si_emplace(&frequencies, c_sv("hundred"), 300); // c_sv() shorthand for csview_new() + cmap_si_emplace(&frequencies, csview_from_n(key, 4), 400); // insert "hund" + cmap_si_emplace_or_assign(&frequencies, csview_from(key), 500); // update + cmap_si_emplace(&frequencies, c_sv("hundred"), 600); // ignored, already inserted + + // Lookup always uses "raw" type API, i.e. csview here. + printf("at(\"hundred\"): %d\n", *cmap_si_at(&frequencies, c_sv("hundred"))); + + c_foreach (i, cmap_si, frequencies) + printf("%s: %d\n", i.ref->first.str, i.ref->second); + + /* Output: */ + // "The quick brown fox jumps over the lazy dog.", length=44 + // at("hundred"): 500 + // hund: 400 + // hundred: 500 + // thousand: 2000 +} \ No newline at end of file diff --git a/stc/csview.h b/stc/csview.h new file mode 100644 index 00000000..a24bac26 --- /dev/null +++ b/stc/csview.h @@ -0,0 +1,157 @@ +/* MIT License + * + * Copyright (c) 2021 Tyge Løvset, NORCE, www.norceresearch.no + * + * 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. + */ +#ifndef CSVIEW_H_INCLUDED +#define CSVIEW_H_INCLUDED + +#include "cstr.h" + +typedef struct { const char* str; size_t size; } csview; +typedef struct { const char *ref; } csview_iter_t; +typedef char csview_value_t; +#define csview_null c_make(csview){"", 0} + +#define csview_new(literal) \ + c_make(csview){literal, sizeof c_make(strlit_t){literal} - 1} +#define c_sv(literal) \ + csview_new(literal) +STC_INLINE csview csview_from(const char* str) + { return c_make(csview){str, strlen(str)}; } +STC_INLINE csview csview_from_n(const char* str, size_t n) + { return c_make(csview){str, n}; } +STC_INLINE csview csview_from_v(csview sv, size_t pos) + { sv.str += pos, sv.size -= pos; return sv; } +STC_INLINE csview csview_from_s(cstr s) + { return c_make(csview){s.str, _cstr_rep(&s)->size}; } +STC_INLINE csview csview_substr(csview sv, size_t pos, size_t n) + { sv.str += pos, sv.size = n; return sv; } + +STC_INLINE size_t csview_size(csview sv) { return sv.size; } +STC_INLINE size_t csview_length(csview sv) { return sv.size; } +STC_INLINE bool csview_empty(csview sv) { return sv.size == 0; } +STC_INLINE void csview_clear(csview* self) { *self = csview_null; } +STC_INLINE const char* csview_front(const csview* self) { return self->str; } +STC_INLINE const char* csview_back(const csview* self) { return self->str + self->size - 1; } + +STC_INLINE size_t csview_find(csview sv, csview needle) + { char* res = c_strnstrn(sv.str, needle.str, sv.size, needle.size); + return res ? res - sv.str : cstr_npos; } +STC_INLINE bool csview_equals(csview sv, csview sv2) + { return sv.size == sv2.size && !memcmp(sv.str, sv2.str, sv.size); } +STC_INLINE bool csview_contains(csview sv, csview sub) + { return c_strnstrn(sv.str, sub.str, sv.size, sub.size) != NULL; } +STC_INLINE bool csview_begins_with(csview sv, csview sub) + { if (sub.size > sv.size) return false; + return !memcmp(sv.str, sub.str, sub.size); } +STC_INLINE bool csview_ends_with(csview sv, csview sub) + { if (sub.size > sv.size) return false; + return !memcmp(sv.str + sv.size - sub.size, sub.str, sub.size); } +STC_INLINE csview_iter_t csview_begin(const csview* self) + { return c_make(csview_iter_t){self->str}; } +STC_INLINE csview_iter_t csview_end(const csview* self) + { return c_make(csview_iter_t){self->str + self->size}; } +STC_INLINE void csview_next(csview_iter_t* it) { ++it->ref; } + +/* cstr interaction with csview: */ + +STC_INLINE cstr cstr_from_v(csview sv) + { return cstr_from_n(sv.str, sv.size); } +STC_INLINE csview cstr_to_v(const cstr* self) + { return c_make(csview){self->str, _cstr_rep(self)->size}; } +STC_INLINE cstr* cstr_assign_v(cstr* self, csview sv) + { return cstr_assign_n(self, sv.str, sv.size); } +STC_INLINE cstr* cstr_append_v(cstr* self, csview sv) + { return cstr_append_n(self, sv.str, sv.size); } +STC_INLINE void cstr_insert_v(cstr* self, size_t pos, csview sv) + { cstr_replace_n(self, pos, 0, sv.str, sv.size); } +STC_INLINE void cstr_replace_v(cstr* self, size_t pos, size_t len, csview sv) + { cstr_replace_n(self, pos, len, sv.str, sv.size); } +STC_INLINE size_t cstr_find_v(cstr s, csview needle) + { char* res = c_strnstrn(s.str, needle.str, cstr_size(s), needle.size); + return res ? res - s.str : cstr_npos; } +STC_INLINE bool cstr_equals_v(cstr s, csview sv) + { return sv.size == cstr_size(s) && !memcmp(s.str, sv.str, sv.size); } +STC_INLINE bool cstr_contains_v(cstr s, csview sub) + { return c_strnstrn(s.str, sub.str, cstr_size(s), sub.size) != NULL; } +STC_INLINE bool cstr_begins_with_v(cstr s, csview sub) + { if (sub.size > cstr_size(s)) return false; + return !memcmp(s.str, sub.str, sub.size); } +STC_INLINE bool cstr_ends_with_v(cstr s, csview sub) + { if (sub.size > cstr_size(s)) return false; + return !memcmp(s.str + cstr_size(s) - sub.size, sub.str, sub.size); } + + +/* ---- Adaptor functions ---- */ + +#define csview_compare_ref(xp, yp) strcmp((xp)->str, (yp)->str) +STC_INLINE bool csview_equals_ref(const csview* a, const csview* b) + { return a->size == b->size && !memcmp(a->str, b->str, a->size); } +#define csview_hash_ref(xp, none) c_default_hash((xp)->str, (xp)->size) + +/* ---- Containers ---- */ + +#define using_cvec_sv() \ + using_cvec(sv, cstr, csview_compare_ref, cstr_del, cstr_from_v, cstr_to_v, csview) +#define using_cdeq_sv() \ + using_cdeq(sv, cstr, csview_compare_ref, cstr_del, cstr_from_v, cstr_to_v, csview) +#define using_clist_sv() \ + using_clist(sv, cstr, csview_compare_ref, cstr_del, cstr_from_v, cstr_to_v, csview) + + +#define using_csmap_svkey(...) c_MACRO_OVERLOAD(using_csmap_svkey, __VA_ARGS__) + +#define using_csmap_svkey_2(X, Mapped) \ + using_csmap_svkey_4(X, Mapped, c_default_del, c_default_fromraw) +#define using_csmap_svkey_3(X, Mapped, mappedDel) \ + using_csmap_svkey_4(X, Mapped, mappedDel, c_no_clone) +#define using_csmap_svkey_4(X, Mapped, mappedDel, mappedClone) \ + using_csmap_svkey_6(X, Mapped, mappedDel, mappedClone, c_default_toraw, Mapped) +#define using_csmap_svkey_6(X, Mapped, mappedDel, mappedFromRaw, mappedToRaw, RawMapped) \ + _c_using_aatree(csmap_##X, csmap_, cstr, Mapped, csview_compare_ref, \ + mappedDel, mappedFromRaw, mappedToRaw, RawMapped, \ + cstr_del, cstr_from_v, cstr_to_v, csview) +#define using_csmap_sv() \ + using_csmap_svkey_6(sv, cstr, cstr_del, cstr_from_v, cstr_to_v, csview) +#define using_csset_sv() \ + _c_using_aatree(csset_sv, csset_, cstr, cstr, csview_compare_ref, \ + @@, @@, @@, void, cstr_del, cstr_from_v, cstr_to_v, csview) + + +#define using_cmap_svkey(...) c_MACRO_OVERLOAD(using_cmap_svkey, __VA_ARGS__) + +#define using_cmap_svkey_2(X, Mapped) \ + using_cmap_svkey_4(X, Mapped, c_default_del, c_default_fromraw) +#define using_cmap_svkey_3(X, Mapped, mappedDel) \ + using_cmap_svkey_4(X, Mapped, mappedDel, c_no_clone) +#define using_cmap_svkey_4(X, Mapped, mappedDel, mappedClone) \ + using_cmap_svkey_6(X, Mapped, mappedDel, mappedClone, c_default_toraw, Mapped) +#define using_cmap_svkey_6(X, Mapped, mappedDel, mappedFromRaw, mappedToRaw, RawMapped) \ + _c_using_chash(cmap_##X, cmap_, cstr, Mapped, csview_equals_ref, csview_hash_ref, \ + mappedDel, mappedFromRaw, mappedToRaw, RawMapped, \ + cstr_del, cstr_from_v, cstr_to_v, csview) +#define using_cmap_sv() \ + using_cmap_svkey_6(sv, cstr, cstr_del, cstr_from_v, cstr_to_v, csview) +#define using_cset_sv() \ + _c_using_chash(cset_sv, cset_, cstr, cstr, csview_equals_ref, csview_hash_ref, \ + @@, @@, @@, void, cstr_del, cstr_from_v, cstr_to_v, csview) + +#endif \ No newline at end of file -- cgit v1.2.3