diff options
| author | Tylo <[email protected]> | 2020-05-23 13:34:30 +0200 |
|---|---|---|
| committer | Tylo <[email protected]> | 2020-05-23 13:34:30 +0200 |
| commit | 0c77264d7e568f7e254ff7ddfe91472734999e26 (patch) | |
| tree | 3739179de141ab3fb40d1706dd29ab01f1eb3026 | |
| parent | 6d3e416286ce24aa720e6403bdab4c914c4b25fa (diff) | |
| download | STC-modified-0c77264d7e568f7e254ff7ddfe91472734999e26.tar.gz STC-modified-0c77264d7e568f7e254ff7ddfe91472734999e26.zip | |
Many minor cstring bug fixes and improvements. Added demos.c and advanced_example.c
| -rw-r--r-- | advanced_example.c | 91 | ||||
| -rw-r--r-- | demos.c | 122 | ||||
| -rw-r--r-- | stc/cstring.h | 37 | ||||
| -rw-r--r-- | stc/cvector.h | 5 |
4 files changed, 242 insertions, 13 deletions
diff --git a/advanced_example.c b/advanced_example.c new file mode 100644 index 00000000..449bddb3 --- /dev/null +++ b/advanced_example.c @@ -0,0 +1,91 @@ +/*To be able to use CMap with a user-defined key-type, you need to define two things: + +1. A hash function; this must be a function that calculates the hash value given an object of the key-type. + +2. A comparison function for equality; this is required because the hash cannot rely on the fact that the hash function will always provide a unique hash value for every distinct key (i.e., it needs to be able to deal with collisions), so it needs a way to compare two given keys for an exact match. + +The difficulty with the hash function is that if your key type consists of several members, you will usually have the hash function calculate hash values for the individual members, and then somehow combine them into one hash value for the entire object. For good performance (i.e., few collisions) you should think carefully about how to combine the individual hash values to ensure you avoid getting the same output for different objects too often. + +Assuming a key-type like this, and want string as value, we define the functions person_make(), person_destroy() and person_compare(): +```*/ +#include <stdio.h> +#include "stc/cmap.h" +#include "stc/cstring.h" + +struct Person { + CString name; + CString surname; + int age; +}; + +struct Person person_make(const char* name, const char* surname, int age) { + struct Person person = {cstring_make(name), cstring_make(surname), age}; + return person; +} + +void person_destroy(struct Person* p) { + cstring_destroy(&p->name); + cstring_destroy(&p->surname); +} +/*``` +In order to use Person as a map key, provide a "view" of your class that owns no resources (e.g. CString): +```*/ +struct PersonView { + const char* name; + const char* surname; + int age; +}; + +struct PersonView person_getView(struct Person* p) { + return (struct PersonView) {p->name.str, p->surname.str, p->age}; +} + +struct Person person_fromView(struct PersonView pv) { + return (struct Person) {cstring_make(pv.name), cstring_make(pv.surname), pv.age}; +} + +int personview_compare(const struct PersonView* x, const struct PersonView* y) { + int c; + c = strcmp(x->name, y->name); if (c != 0) return c; + c = strcmp(x->surname, y->surname); if (c != 0) return c; + return x->age - y->age; +} +/*``` +And a hash function that combines the three member's hashes: +```*/ +size_t personview_hash(const struct PersonView* pv, size_t ignore) { + // http://stackoverflow.com/a/1646913/126995 + size_t res = 17; + res = res * 31 + c_defaultHash(pv->name, strlen(pv->name)); + res = res * 31 + c_defaultHash(pv->surname, strlen(pv->surname)); + res = res * 31 + c_defaultHash(&pv->age, sizeof(pv->age)); + return res; +} +/*``` +With this in place, we can declare the map Person -> int: +```*/ +declare_CMap(ex, struct Person, int, c_noDestroy, person_destroy, + struct PersonView, personview_hash, personview_compare, + person_getView, person_fromView); +/*``` +Note we use struct PersonView to put keys in the map, but keys are stored as struct Person with proper dynamically allocated CStrings to store name and surname. +```*/ +int main() +{ + CMap_ex m6 = cmap_init; + cmap_ex_put(&m6, (struct PersonView){"John", "Doe", 24}, 1001); + cmap_ex_put(&m6, (struct PersonView){"Jane", "Doe", 21}, 1002); + cmap_ex_put(&m6, (struct PersonView){"John", "Travolta", 66}, 1003); + + c_foreach (it, cmap_ex, m6) { + if (cstring_equals(it.item->key.name, "John")) + printf("%s %s %d -> %d\n", it.item->key.name.str, it.item->key.surname.str, it.item->key.age, + it.item->value); + } + + cmap_ex_destroy(&m6); +} +/*``` +CMap uses personview_hash() for hash value calculations, and the personview_compare() for equality checks. The cmap_ex_destroy() function will free CStrings name, surname and the value for each item in the map, in addition to the CMap hash table itself. +*/ + diff --git a/demos.c b/demos.c new file mode 100644 index 00000000..82505241 --- /dev/null +++ b/demos.c @@ -0,0 +1,122 @@ +#include "stc/cstring.h" +#include "stc/cvector.h" +#include "stc/cmap.h" + + +void stringdemo() { + CString cs = cstring_make("one-nine-three-seven-five"); + printf("%s.\n", cs.str); + + cstring_insert(&cs, 3, "-two"); + printf("%s.\n", cs.str); + + cstring_erase(&cs, 7, 5); // -nine + printf("%s.\n", cs.str); + + cstring_replace(&cs, 0, "seven", "four"); + printf("%s.\n", cs.str); + cstring_take(&cs, cstring_makeFmt("%s *** %s", cs.str, cs.str)); + printf("%s.\n", cs.str); + + printf("find: %s\n", cs.str + cstring_find(cs, 0, "four")); + + // reassign: + cstring_assign(&cs, "one two three four five six seven"); + cstring_append(&cs, " eight"); + printf("append: %s\n", cs.str); + + cstring_destroy(&cs); +} + + +declare_CVector(ix, int64_t); // ix is just an example tag name. + +void vectordemo() { + CVector_ix bignums = cvector_init; // = (CVector_ix) cvector_init; if initializing after declaration. + cvector_ix_reserve(&bignums, 100); + for (size_t i = 0; i<100; ++i) + cvector_ix_pushBack(&bignums, i * i * i); + cvector_ix_popBack(&bignums); // erase the last + + uint64_t value; + for (size_t i = 0; i < cvector_size(bignums); ++i) + value = bignums.data[i]; + cvector_ix_destroy(&bignums); +} + + + +declare_CVector(cs, CString, cstring_destroy, cstring_compare); // supply inline destructor of values + +void stringvectordemo() { + CVector_cs names = cvector_init; + cvector_cs_pushBack(&names, cstring_make("Mary")); + cvector_cs_pushBack(&names, cstring_make("Joe")); + cvector_cs_pushBack(&names, cstring_make("Chris")); + cstring_assign(&names.data[1], "Anna"); // replace Joe + printf("names[1]: %s\n", names.data[1].str); + + cvector_cs_sort(&names); // Sort the array + c_foreach (i, cvector_cs, names) + printf("name: %s\n", i.item->str); + cvector_cs_destroy(&names); +} + + +declare_CMap(ii, int, int); + +void mapdemo() { + CMap_ii nums = cmap_init; + cmap_ii_put(&nums, 8, 64); + cmap_ii_put(&nums, 11, 121); + + printf("%d\n", cmap_ii_get(nums, 8)->value); + cmap_ii_destroy(&nums); +} + + + +declare_CMap_stringkey(si, int); // Shorthand macro for the general declare_CMap expansion. + +void stringmapdemo() { + CMap_si nums = cmap_init; + cmap_si_put(&nums, "Hello", 64); + cmap_si_put(&nums, "Groovy", 121); + cmap_si_put(&nums, "Groovy", 200); // overwrite previous + + // iterate the map: + for (cmap_si_iter_t i = cmap_si_begin(&nums); i.item; i = cmap_si_next(i)) + printf("%s: %d\n", i.item->key.str, i.item->value); + + // or rather use the short form: + c_foreach (i, cmap_si, nums) + printf("%s: %d\n", i.item->key.str, i.item->value); + + cmap_si_destroy(&nums); +} + + +declare_CMap_stringkey(ss, CString, cstring_destroy); + +void stringmapdemo2() { + CMap_ss table = cmap_init; + cmap_ss_put(&table, "Make", cstring_make("my")); + cmap_ss_put(&table, "Sunny", cstring_make("day")); + printf("Sunny: %s\n", cmap_ss_get(table, "Sunny")->value.str); + cmap_ss_erase(&table, "Make"); + + printf("size %d\n", cmap_size(table)); + cmap_ss_destroy(&table); // frees key and value CStrings, and hash table (CVector). +} + + + +int main() +{ + stringdemo(); + vectordemo(); + stringvectordemo(); + mapdemo(); + stringmapdemo(); + stringmapdemo2(); +} diff --git a/stc/cstring.h b/stc/cstring.h index 766eb407..f03cbc6e 100644 --- a/stc/cstring.h +++ b/stc/cstring.h @@ -128,23 +128,15 @@ cstring_makeFmt(const char* fmt, ...) { }
-static inline CString
-cstring_move(CString* self) {
- CString mv = *self;
- *self = cstring_init;
- return mv;
-}
-
static inline void
cstring_clear(CString* self) {
- CString s = cstring_init;
cstring_destroy(self);
- *self = s;
+ *self = cstring_init;
}
static inline CString*
cstring_assignN(CString* self, const char* str, size_t len) {
- if (len) {
+ if (len || cstring_capacity(*self)) {
cstring_reserve(self, len);
memmove(self->str, str, len);
self->str[_cstring_size(*self) = len] = '\0';
@@ -156,11 +148,34 @@ static inline CString* cstring_assign(CString* self, const char* str) {
return cstring_assignN(self, str, strlen(str));
}
+
static inline CString*
-cstring_assignS(CString* self, CString s) {
+cstring_copy(CString* self, CString s) {
return cstring_assignN(self, s.str, cstring_size(s));
}
+static inline CString*
+cstring_take(CString* self, CString s) {
+ if (self->str != s.str && cstring_capacity(*self))
+ free(_cstring_rep(self));
+ self->str = s.str;
+ return self;
+}
+
+static inline CString
+cstring_release(CString* self) {
+ CString tmp = *self;
+ *self = cstring_init;
+ return tmp;
+}
+/* cstring_move(&s1, &s2) short for: cstring_take(&s1, cstring_release(&s2)) */
+static inline CString*
+cstring_move(CString* self, CString* s) {
+ cstring_take(self, *s);
+ *s = cstring_init;
+ return self;
+}
+
static inline CString*
cstring_appendN(CString* self, const char* str, size_t len) {
diff --git a/stc/cvector.h b/stc/cvector.h index 86bdfb13..fe9e2d7d 100644 --- a/stc/cvector.h +++ b/stc/cvector.h @@ -173,9 +173,10 @@ cvector_##tag##_find(CVector_##tag cv, ValueRaw rawValue) { \ size_t n = cvector_size(cv); \
cvector_##tag##_rawvalue_t r; \
for (size_t i = 0; i < n; ++i) { \
- if (valueCompareRaw((r = valueGetRaw(&cv.data[i]), &r), &rawValue) == 0) return i; \
+ ValueRaw r = valueGetRaw(&cv.data[i]); \
+ if (valueCompareRaw(&r, &rawValue) == 0) return i; \
} \
- return c_npos; \
+ return (size_t) (-1); /*SIZE_MAX;*/ \
} \
\
STC_API int \
|
