//#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG #include #include #include #include #include #include #include "httpd_utils/session_kvmap.h" static const char *TAG = "sess_kvmap"; // this struct is opaque, a stub like this is sufficient for the head pointer. struct sess_kv_entry; /** Session head structure, dynamically allocated */ SLIST_HEAD(sess_kv_map, sess_kv_entry); struct sess_kv_entry { SLIST_ENTRY(sess_kv_entry) link; char key[SESS_KVMAP_KEY_LEN]; void *value; sess_kv_free_func_t free_fn; }; struct sess_kv_map *sess_kv_map_alloc(void) { ESP_LOGD(TAG, "kv store alloc"); struct sess_kv_map *map = malloc(sizeof(struct sess_kv_map)); assert(map); SLIST_INIT(map); return map; } void sess_kv_map_free(void *head_v) { struct sess_kv_map* head = head_v; ESP_LOGD(TAG, "kv store free"); assert(head); struct sess_kv_entry *item, *tmp; SLIST_FOREACH_SAFE(item, head, link, tmp) { if (item->free_fn) { item->free_fn(item->value); free(item); } } free(head); } void * sess_kv_map_get(struct sess_kv_map *head, const char *key) { assert(head); assert(key); ESP_LOGD(TAG, "kv store get %s", key); struct sess_kv_entry *item; SLIST_FOREACH(item, head, link) { if (0==strcmp(item->key, key)) { ESP_LOGD(TAG, "got ok"); return item->value; } } ESP_LOGD(TAG, "not found in store"); return NULL; } void * sess_kv_map_take(struct sess_kv_map *head, const char *key) { assert(head); assert(key); ESP_LOGD(TAG, "kv store take %s", key); struct sess_kv_entry *item; SLIST_FOREACH(item, head, link) { if (0==strcmp(item->key, key)) { item->key[0] = 0; item->free_fn = NULL; ESP_LOGD(TAG, "taken ok"); return item->value; } } ESP_LOGD(TAG, "not found in store"); return NULL; } esp_err_t sess_kv_map_remove(struct sess_kv_map *head, const char *key) { assert(head); assert(key); ESP_LOGD(TAG, "kv store remove %s", key); struct sess_kv_entry *item; SLIST_FOREACH(item, head, link) { if (0==strcmp(item->key, key)) { if (item->free_fn) { item->free_fn(item->value); } item->key[0] = 0; item->value = NULL; item->free_fn = NULL; return ESP_OK; } } ESP_LOGD(TAG, "couldn't remove, not found: %s", key); return ESP_ERR_NOT_FOUND; } esp_err_t sess_kv_map_set(struct sess_kv_map *head, const char *key, void *value, sess_kv_free_func_t free_fn) { assert(head); assert(key); ESP_LOGD(TAG, "kv set value for key %s", key); size_t key_len = strlen(key); if (key_len > SESS_KVMAP_KEY_LEN-1) { ESP_LOGE(TAG, "Key too long: %s", key); // discard illegal key return ESP_FAIL; } if (key_len == 0) { ESP_LOGE(TAG, "Key too short: \"%s\"", key); // discard illegal key return ESP_FAIL; } struct sess_kv_entry *item = NULL; struct sess_kv_entry *empty_item = NULL; // found item with no content SLIST_FOREACH(item, head, link) { ESP_LOGD(TAG, "test item with key %s, ptr %p > %p", item->key, item, item->link.sle_next); if (0 == item->key[0]) { ESP_LOGD(TAG, "found an empty slot"); empty_item = item; } else if (0==strcmp(item->key, key)) { ESP_LOGD(TAG, "old value replaced"); if (item->free_fn) { item->free_fn(item->value); } item->value = value; item->free_fn = free_fn; return ESP_OK; } else { ESP_LOGD(TAG, "skip this one"); } } struct sess_kv_entry *new_item = NULL; // insert new or reuse an empty item if (empty_item) { new_item = empty_item; ESP_LOGD(TAG, "empty item reused (%p)", new_item); } else { ESP_LOGD(TAG, "alloc new item"); // key not found, add a new entry. new_item = malloc(sizeof(struct sess_kv_entry)); if (!new_item) { ESP_LOGE(TAG, "New entry alloc failed"); return ESP_ERR_NO_MEM; } } strcpy(new_item->key, key); new_item->free_fn = free_fn; new_item->value = value; if (!empty_item) { ESP_LOGD(TAG, "insert new item into list"); // this item was malloc'd SLIST_INSERT_HEAD(head, new_item, link); } return ESP_OK; }