From: Olivier Matz Date: Fri, 30 Mar 2018 20:47:23 +0000 (+0200) Subject: new keyval iterator X-Git-Url: http://git.droids-corp.org/?a=commitdiff_plain;h=df55a63b65af4021b758a23f2fe5b224d2f5d3e3;p=protos%2Flibecoli.git new keyval iterator --- diff --git a/lib/ecoli_keyval.c b/lib/ecoli_keyval.c index d4a9a51..164c8b4 100644 --- a/lib/ecoli_keyval.c +++ b/lib/ecoli_keyval.c @@ -46,6 +46,12 @@ struct ec_keyval { struct ec_keyval_elt_ref_list *table; }; +struct ec_keyval_iter { + const struct ec_keyval *keyval; + size_t cur_idx; + const struct ec_keyval_elt_ref *cur_ref; +}; + struct ec_keyval *ec_keyval(void) { struct ec_keyval *keyval; @@ -260,23 +266,106 @@ size_t ec_keyval_len(const struct ec_keyval *keyval) return keyval->len; } -void ec_keyval_dump(FILE *out, const struct ec_keyval *keyval) +void +ec_keyval_iter_free(struct ec_keyval_iter *iter) { - struct ec_keyval_elt_ref *ref; + ec_free(iter); +} + +struct ec_keyval_iter * +ec_keyval_iter(const struct ec_keyval *keyval) +{ + struct ec_keyval_iter *iter = NULL; + + iter = ec_calloc(1, sizeof(*iter)); + if (iter == NULL) + return NULL; + + iter->keyval = keyval; + iter->cur_idx = 0; + iter->cur_ref = NULL; + + ec_keyval_iter_next(iter); + + return iter; +} + +void +ec_keyval_iter_next(struct ec_keyval_iter *iter) +{ + const struct ec_keyval_elt_ref *ref; size_t i; + i = iter->cur_idx; + if (i == iter->keyval->table_size) + return; /* no more element */ + + if (iter->cur_ref != NULL) { + ref = LIST_NEXT(iter->cur_ref, next); + if (ref != NULL) { + iter->cur_ref = ref; + return; + } + i++; + } + + for (; i < iter->keyval->table_size; i++) { + LIST_FOREACH(ref, &iter->keyval->table[i], next) { + iter->cur_idx = i; + iter->cur_ref = LIST_FIRST(&iter->keyval->table[i]); + return; + } + } + + iter->cur_idx = iter->keyval->table_size; + iter->cur_ref = NULL; +} + +bool +ec_keyval_iter_valid(const struct ec_keyval_iter *iter) +{ + if (iter == NULL || iter->cur_ref == NULL) + return false; + + return true; +} + +const char * +ec_keyval_iter_get_key(const struct ec_keyval_iter *iter) +{ + if (iter == NULL || iter->cur_ref == NULL) + return NULL; + + return iter->cur_ref->elt->key; +} + +void * +ec_keyval_iter_get_val(const struct ec_keyval_iter *iter) +{ + if (iter == NULL || iter->cur_ref == NULL) + return NULL; + + return iter->cur_ref->elt->val; +} + +void ec_keyval_dump(FILE *out, const struct ec_keyval *keyval) +{ + struct ec_keyval_iter *iter; + if (keyval == NULL) { fprintf(out, "empty keyval\n"); return; } fprintf(out, "keyval:\n"); - for (i = 0; i < keyval->table_size; i++) { - LIST_FOREACH(ref, &keyval->table[i], next) { - fprintf(out, " %s: %p\n", - ref->elt->key, ref->elt->val); - } + for (iter = ec_keyval_iter(keyval); + ec_keyval_iter_valid(iter); + ec_keyval_iter_next(iter)) { + fprintf(out, " %s: %p\n", + ec_keyval_iter_get_key(iter), + ec_keyval_iter_get_val(iter)); } + ec_keyval_iter_free(iter); } struct ec_keyval *ec_keyval_dup(const struct ec_keyval *keyval) @@ -340,9 +429,13 @@ EC_INIT_REGISTER(ec_keyval_init); static int ec_keyval_testcase(void) { struct ec_keyval *keyval, *dup; + struct ec_keyval_iter *iter; char *val; - size_t i; + size_t i, count; int ret, testres = 0; + FILE *f = NULL; + char *buf = NULL; + size_t buflen = 0; keyval = ec_keyval(); if (keyval == NULL) { @@ -350,6 +443,15 @@ static int ec_keyval_testcase(void) return -1; } + count = 0; + for (iter = ec_keyval_iter(keyval); + ec_keyval_iter_valid(iter); + ec_keyval_iter_next(iter)) { + count++; + } + ec_keyval_iter_free(iter); + testres |= EC_TEST_CHECK(count == 0, "invalid count in iterator"); + testres |= EC_TEST_CHECK(ec_keyval_len(keyval) == 0, "bad keyval len"); ret = ec_keyval_set(keyval, "key1", "val1", NULL); testres |= EC_TEST_CHECK(ret == 0, "cannot set key"); @@ -383,13 +485,32 @@ static int ec_keyval_testcase(void) testres |= EC_TEST_CHECK(ec_keyval_has_key(keyval, "key1"), "key1 should be in keyval"); + f = open_memstream(&buf, &buflen); + if (f == NULL) + goto fail; + ec_keyval_dump(f, NULL); + fclose(f); + f = NULL; + free(buf); + buf = NULL; + + f = open_memstream(&buf, &buflen); + if (f == NULL) + goto fail; + ec_keyval_dump(f, keyval); + fclose(f); + f = NULL; + free(buf); + buf = NULL; + ret = ec_keyval_del(keyval, "key1"); - testres |= EC_TEST_CHECK(ret == 0, "cannot del key"); + testres |= EC_TEST_CHECK(ret == 0, "cannot del key1"); testres |= EC_TEST_CHECK(ec_keyval_len(keyval) == 1, "invalid keyval len"); - - ec_keyval_dump(stdout, NULL); - ec_keyval_dump(stdout, keyval); + ret = ec_keyval_del(keyval, "key2"); + testres |= EC_TEST_CHECK(ret == 0, "cannot del key2"); + testres |= EC_TEST_CHECK(ec_keyval_len(keyval) == 0, + "invalid keyval len"); for (i = 0; i < 100; i++) { char key[8]; @@ -412,6 +533,15 @@ static int ec_keyval_testcase(void) dup = NULL; } + count = 0; + for (iter = ec_keyval_iter(keyval); + ec_keyval_iter_valid(iter); + ec_keyval_iter_next(iter)) { + count++; + } + ec_keyval_iter_free(iter); + testres |= EC_TEST_CHECK(count == 100, "invalid count in iterator"); + /* einval */ ret = ec_keyval_set(keyval, NULL, "val1", NULL); testres |= EC_TEST_CHECK(ret == -1, "should not be able to set key"); @@ -421,6 +551,13 @@ static int ec_keyval_testcase(void) ec_keyval_free(keyval); return testres; + +fail: + ec_keyval_free(keyval); + if (f) + fclose(f); + free(buf); + return -1; } /* LCOV_EXCL_STOP */ diff --git a/lib/ecoli_keyval.h b/lib/ecoli_keyval.h index 82629fe..a3e9400 100644 --- a/lib/ecoli_keyval.h +++ b/lib/ecoli_keyval.h @@ -18,6 +18,7 @@ typedef void (*ec_keyval_elt_free_t)(void *); struct ec_keyval; +struct ec_keyval_iter; /** * Create a hash table. @@ -125,4 +126,79 @@ struct ec_keyval *ec_keyval_dup(const struct ec_keyval *keyval); */ void ec_keyval_dump(FILE *out, const struct ec_keyval *keyval); +/** + * Iterate the elements in the hash table. + * + * The typical usage is as below: + * + * // dump elements + * for (iter = ec_keyval_iter(keyval); + * ec_keyval_iter_valid(iter); + * ec_keyval_iter_next(iter)) { + * printf(" %s: %p\n", + * ec_keyval_iter_get_key(iter), + * ec_keyval_iter_get_val(iter)); + * } + * ec_keyval_iter_free(iter); + * + * @param keyval + * The hash table. + * @return + * An iterator, or NULL on error (errno is set). + */ +struct ec_keyval_iter * +ec_keyval_iter(const struct ec_keyval *keyval); + +/** + * Make the iterator point to the next element in the hash table. + * + * @param iter + * The hash table iterator. + */ +void ec_keyval_iter_next(struct ec_keyval_iter *iter); + +/** + * Free the iterator. + * + * @param iter + * The hash table iterator. + */ +void ec_keyval_iter_free(struct ec_keyval_iter *iter); + +/** + * Check if the iterator points to a valid element. + * + * @param iter + * The hash table iterator. + * @return + * true if the element is valid, else false. + */ +bool +ec_keyval_iter_valid(const struct ec_keyval_iter *iter); + +/** + * Get the key of the current element. + * + * @param iter + * The hash table iterator. + * @return + * The current element key, or NULL if the iterator points to an + * invalid element. + */ +const char * +ec_keyval_iter_get_key(const struct ec_keyval_iter *iter); + +/** + * Get the value of the current element. + * + * @param iter + * The hash table iterator. + * @return + * The current element value, or NULL if the iterator points to an + * invalid element. + */ +void * +ec_keyval_iter_get_val(const struct ec_keyval_iter *iter); + + #endif