From cad2f1c103ae906417b0cd55dd8eea57b6a553ab Mon Sep 17 00:00:00 2001 From: Riadh Ghaddab Date: Tue, 25 Mar 2025 18:52:50 +0100 Subject: [PATCH] settings: add new API function settings_get_val_len() Add a function to get the value's length of a Key. If it doesn't exist returns 0. Add ZMS implementation for csi_get_val_len() and a default implementation for the other storage systems. Add some functional tests to verify it. Signed-off-by: Riadh Ghaddab --- include/zephyr/settings/settings.h | 17 ++++++ subsys/settings/src/settings_store.c | 55 +++++++++++++++++++ subsys/settings/src/settings_zms.c | 42 +++++++++++++- .../functional/src/settings_basic_test.c | 27 +++++++++ 4 files changed, 140 insertions(+), 1 deletion(-) diff --git a/include/zephyr/settings/settings.h b/include/zephyr/settings/settings.h index 9a810b5920f..ac8d6fc4bb4 100644 --- a/include/zephyr/settings/settings.h +++ b/include/zephyr/settings/settings.h @@ -289,6 +289,14 @@ int settings_load_subtree(const char *subtree); */ ssize_t settings_load_one(const char *name, void *buf, size_t buf_len); +/** + * Get the data length of the value relative to the key + * + * @param[in] key Name/key of the settings item. + * @return length of value if item exists, 0 if not and negative value on failure. + */ +ssize_t settings_get_val_len(const char *key); + /** * Callback function used for direct loading. * Used by @ref settings_load_subtree_direct function. @@ -479,6 +487,15 @@ struct settings_store_itf { * - buf_len - Length of buf. */ + ssize_t (*csi_get_val_len)(struct settings_store *cs, const char *name); + /**< Gets the value's length associated to the Key defined by name. + * It returns 0 if the Key/Value doesn't exist. + * + * Parameters: + * - cs - Corresponding backend handler node. + * - name - Key in string format. + */ + int (*csi_save_start)(struct settings_store *cs); /**< Handler called before an export operation. * diff --git a/subsys/settings/src/settings_store.c b/subsys/settings/src/settings_store.c index 801d56421e4..49775a7fc80 100644 --- a/subsys/settings/src/settings_store.c +++ b/subsys/settings/src/settings_store.c @@ -94,6 +94,7 @@ struct default_param { size_t *val_len; }; +/* Default callback to set a Key/Value pair */ static int settings_set_default_cb(const char *name, size_t len, settings_read_cb read_cb, void *cb_arg, void *param) { @@ -111,6 +112,60 @@ static int settings_set_default_cb(const char *name, size_t len, settings_read_c return rc; } +/* Default callback to get the value's length of the Key defined by name. + * Returns the value's length in the provided `param` pointer + */ +static int settings_get_val_len_default_cb(const char *name, size_t len, + [[maybe_unused]] settings_read_cb read_cb, + [[maybe_unused]] void *cb_arg, void *param) +{ + const char *next; + size_t name_len; + size_t *val_len = (size_t *)param; + + name_len = settings_name_next(name, &next); + if (name_len == 0) { + *val_len = len; + } + + return 0; +} + +/* Gets the value's size if the Key defined by name is in the persistent storage, + * Returns 0 if the Key doesn't exist. + */ +ssize_t settings_get_val_len(const char *name) +{ + struct settings_store *cs; + int rc = 0; + size_t val_len = 0; + + /* + * for every config store that supports this function + * get the value's length. + */ + k_mutex_lock(&settings_lock, K_FOREVER); + SYS_SLIST_FOR_EACH_CONTAINER(&settings_load_srcs, cs, cs_next) { + if (cs->cs_itf->csi_get_val_len) { + val_len = cs->cs_itf->csi_get_val_len(cs, name); + } else { + const struct settings_load_arg arg = { + .subtree = name, + .cb = &settings_get_val_len_default_cb, + .param = &val_len + }; + rc = cs->cs_itf->csi_load(cs, &arg); + } + } + k_mutex_unlock(&settings_lock); + + if (rc >= 0) { + return val_len; + } + + return rc; +} + /* Load a single key/value from persistent storage */ ssize_t settings_load_one(const char *name, void *buf, size_t buf_len) { diff --git a/subsys/settings/src/settings_zms.c b/subsys/settings/src/settings_zms.c index 3fd111c2199..99b783672cb 100644 --- a/subsys/settings/src/settings_zms.c +++ b/subsys/settings/src/settings_zms.c @@ -33,11 +33,13 @@ static int settings_zms_save(struct settings_store *cs, const char *name, const size_t val_len); static void *settings_zms_storage_get(struct settings_store *cs); static int settings_zms_get_last_hash_ids(struct settings_zms *cf); +static ssize_t settings_zms_get_val_len(struct settings_store *cs, const char *name); static struct settings_store_itf settings_zms_itf = {.csi_load = settings_zms_load, .csi_load_one = settings_zms_load_one, .csi_save = settings_zms_save, - .csi_storage_get = settings_zms_storage_get}; + .csi_storage_get = settings_zms_storage_get, + .csi_get_val_len = settings_zms_get_val_len}; static ssize_t settings_zms_read_fn(void *back_end, void *data, size_t len) { @@ -530,6 +532,44 @@ no_ll_update: return 0; } +static ssize_t settings_zms_get_val_len(struct settings_store *cs, const char *name) +{ + struct settings_zms *cf = CONTAINER_OF(cs, struct settings_zms, cf_store); + char r_name[SETTINGS_MAX_NAME_LEN + SETTINGS_EXTRA_LEN + 1]; + ssize_t rc = 0; + uint32_t name_hash; + + /* verify that name is not NULL */ + if (!name) { + return -EINVAL; + } + + name_hash = sys_hash32(name, strlen(name)) & ZMS_HASH_MASK; + for (int i = 0; i <= cf->hash_collision_num; i++) { + name_hash = ZMS_UPDATE_COLLISION_NUM(name_hash, i); + /* Get the name entry from ZMS */ + rc = zms_read(&cf->cf_zms, ZMS_NAME_ID_FROM_HASH(name_hash), r_name, + sizeof(r_name) - 1); + if (rc <= 0) { + /* Name doesn't exist */ + continue; + } + /* Found a name, this might not include a trailing \0 */ + r_name[rc] = '\0'; + if (strcmp(name, r_name)) { + /* Names are not equal let's continue to the next collision hash + * if it exists. + */ + continue; + } + /* At this steps the names are equal, let's read the data size*/ + return zms_get_data_length(&cf->cf_zms, + ZMS_NAME_ID_FROM_HASH(name_hash) + ZMS_DATA_ID_OFFSET); + } + + return 0; +} + static int settings_zms_get_last_hash_ids(struct settings_zms *cf) { struct settings_hash_linked_list settings_element; diff --git a/tests/subsys/settings/functional/src/settings_basic_test.c b/tests/subsys/settings/functional/src/settings_basic_test.c index 1d8349941b9..03ff3eba8a8 100644 --- a/tests/subsys/settings/functional/src/settings_basic_test.c +++ b/tests/subsys/settings/functional/src/settings_basic_test.c @@ -240,13 +240,22 @@ ZTEST(settings_functional, test_register_and_loading) { int rc, err; uint8_t val = 0; + ssize_t val_len = 0; rc = settings_subsys_init(); zassert_true(rc == 0, "subsys init failed"); + /* Check that key that corresponds to val2 do not exist in storage */ + val_len = settings_get_val_len("ps/ss/ss/val2"); + zassert_true((val_len == 0), "Failure: key should not exist"); + settings_save_one("ps/ss/ss/val2", &val, sizeof(uint8_t)); + /* Check that the key that corresponds to val2 exists in storage */ + val_len = settings_get_val_len("ps/ss/ss/val2"); + zassert_true((val_len == 1), "Failure: key should exist"); + memset(&data, 0, sizeof(struct stored_data)); rc = settings_register(&val1_settings); @@ -279,7 +288,16 @@ ZTEST(settings_functional, test_register_and_loading) err = (data.en1) && (data.en2) && (!data.en3); zassert_true(err, "wrong data enable found"); + /* Check that key that corresponds to val3 do not exist in storage */ + val_len = settings_get_val_len("ps/ss/val3"); + zassert_true((val_len == 0), "Failure: key should not exist"); + settings_save_one("ps/ss/val3", &val, sizeof(uint8_t)); + + /* Check that the key that corresponds to val3 exists in storage */ + val_len = settings_get_val_len("ps/ss/val3"); + zassert_true((val_len == 1), "Failure: key should exist"); + memset(&data, 0, sizeof(struct stored_data)); /* when we load settings now data.val2 and data.val1 should receive a * value @@ -310,7 +328,16 @@ ZTEST(settings_functional, test_register_and_loading) err = (data.en1) && (data.en2) && (data.en3); zassert_true(err, "wrong data enable found"); + /* Check that key that corresponds to val1 do not exist in storage */ + val_len = settings_get_val_len("ps/val1"); + zassert_true((val_len == 0), "Failure: key should not exist"); + settings_save_one("ps/val1", &val, sizeof(uint8_t)); + + /* Check that the key that corresponds to val1 exists in storage */ + val_len = settings_get_val_len("ps/val1"); + zassert_true((val_len == 1), "Failure: key should exist"); + memset(&data, 0, sizeof(struct stored_data)); /* when we load settings all data should receive a value loaded */ rc = settings_load();