subsys/settings: stream-style encoding and decoding to/from storage
This patch reworks routines used to store and read the settings data.
Provide stream-style encoding and decoding to/from flash, so the the
API only requires a pointer to binary data, and the settings
implementation takes care of encoding/decoding to/from base64 and
writing/reading to/from flash on the fly. This would eliminate the
need of a separate base64 value buffer on the application-side, thereby
further contributing to the stack footprint reduction.
Above changes allows to remove:
256-byte value length limitation.
removing enum settings_type usage so all settings data are treated
now as a byte array (i.e. what's previously SETTINGS_BYTES)
Introduced routine settings_val_read_cb for read and decode the
settings data from storage inside h_set handler implementations.
h_set settings handler now provide persistent value's context
used along with read routine instead of immediately value.
Signed-off-by: Andrzej Puzdrowski <andrzej.puzdrowski@nordicsemi.no>
diff --git a/include/settings/settings.h b/include/settings/settings.h
index 5fbe3c4..02826ca 100644
--- a/include/settings/settings.h
+++ b/include/settings/settings.h
@@ -8,6 +8,7 @@
#ifndef ZEPHYR_INCLUDE_SETTINGS_SETTINGS_H_
#define ZEPHYR_INCLUDE_SETTINGS_SETTINGS_H_
+#include <sys/types.h>
#include <misc/util.h>
#include <misc/slist.h>
#include <stdint.h>
@@ -35,75 +36,57 @@
#define SETTINGS_NMGR_OP 0
/**
- * Type of settings value.
- */
-enum settings_type {
- SETTINGS_NONE = 0,
- SETTINGS_INT8,
- SETTINGS_INT16,
- SETTINGS_INT32,
- SETTINGS_INT64,
- SETTINGS_STRING,
- SETTINGS_BYTES,
- SETTINGS_FLOAT,
- SETTINGS_DOUBLE,
- SETTINGS_BOOL,
-} __attribute__((__packed__));
-
-/**
- * Parameter to commit handler describing where data is going to.
- */
-enum settings_export_tgt {
- SETTINGS_EXPORT_PERSIST, /* Value is to be persisted. */
- SETTINGS_EXPORT_SHOW /* Value is to be displayed. */
-};
-
-/**
* @struct settings_handler
* Config handlers for subtree implement a set of handler functions.
- * These are registered using a call to settings_register().
- *
- * @param settings_handler::node Linked list node info for module internal usage.
- *
- * @param settings_handler::name Name of subtree.
- *
- * @param settings_handler::h_get Get values handler of settings items
- * identified by keyword names.Parameters:
- * - argc - count of item in argv.
- * - argv - array of pointers to keyword names.
- * - val - buffer for a value.
- * - val_len_max - size of that buffer.
- *
- * @param settings_handler::h_set Set value handler of settings items
- * identified by keyword names. Parameters:
- * - argc - count of item in argv, argv - array of pointers to keyword names.
- * - val- pointer to value to be set.
- *
- * @param settings_handler::h_commit This handler gets called after settings
- * has been loaded in full. User might use it to apply setting to
- * the application.
- *
- * @param settings_handler::h_export This gets called to dump all current
- * settings items.
- * This happens when settings_save() tries to save the settings. Parameters:
- * - tgt: indicates where data is going to.
- * - Export_function: the pointer to the internal function which appends
- * a single key-value pair to persisted settings. Don't store duplicated
- * value. The name is subtree/key string, val is the string with
- * value.
- *
- * @remarks The User might limit a implementations of handler to serving only
- * one keyword at one call - what will impose limit to get/set values using full
- * subtree/key name.
+ * These are registered using a call to @ref settings_register.
*/
struct settings_handler {
sys_snode_t node;
+ /**< Linked list node info for module internal usage. */
+
char *name;
- char *(*h_get)(int argc, char **argv, char *val, int val_len_max);
- int (*h_set)(int argc, char **argv, char *val);
+ /**< Name of subtree. */
+
+ int (*h_get)(int argc, char **argv, char *val, int val_len_max);
+ /**< Get values handler of settings items identified by keyword names.
+ *
+ * Parameters:
+ * - argc - count of item in argv.
+ * - argv - array of pointers to keyword names.
+ * - val - buffer for a value.
+ * - val_len_max - size of that buffer.
+ */
+
+ int (*h_set)(int argc, char **argv, void *value_ctx);
+ /**< Set value handler of settings items identified by keyword names.
+ *
+ * Parameters:
+ * - argc - count of item in argv, argv - array of pointers to keyword
+ * names.
+ * - value_ctx - pointer to the value contex which is used paramiter
+ * for data extracting routine (@ref settings_val_read_cb).
+ */
+
int (*h_commit)(void);
- int (*h_export)(int (*export_func)(const char *name, char *val),
- enum settings_export_tgt tgt);
+ /**< This handler gets called after settings has been loaded in full.
+ * User might use it to apply setting to the application.
+ */
+
+ int (*h_export)(int (*export_func)(const char *name, void *val,
+ size_t val_len));
+ /**< This gets called to dump all current settings items.
+ *
+ * This happens when @ref settings_save tries to save the settings.
+ * Parameters:
+ * - export_func: the pointer to the internal function which appends
+ * a single key-value pair to persisted settings. Don't store
+ * duplicated value. The name is subtree/key string, val is the string
+ * with value.
+ *
+ * @remarks The User might limit a implementations of handler to serving
+ * only one keyword at one call - what will impose limit to get/set
+ * values using full subtree/key name.
+ */
};
/**
@@ -148,29 +131,33 @@
* changed value).
*
* @param name Name/key of the settings item.
- * @param var Value of the settings item.
+ * @param value Pointer to the value of the settings item. This value will
+ * be transferred to the @ref settings_handler::h_export handler implementation.
+ * @param val_len Length of the value.
*
* @return 0 on success, non-zero on failure.
*/
-int settings_save_one(const char *name, char *var);
+int settings_save_one(const char *name, void *value, size_t val_len);
/**
- * Set settings item identified by @p name to be value @p val_str.
+ * Set settings item identified by @p name to be value @p value.
* This finds the settings handler for this subtree and calls it's
* set handler.
*
* @param name Name/key of the settings item.
- * @param val_str Value of the settings item.
+ * @param value Pointer to the value of the settings item. This value will
+ * be transferred to the @ref settings_handler::h_set handler implementation.
+ * @param len Length of value string.
*
* @return 0 on success, non-zero on failure.
*/
-int settings_set_value(char *name, char *val_str);
+int settings_set_value(char *name, void *value, size_t len);
/**
* Get value of settings item identified by @p name.
* This calls the settings handler h_get for the subtree.
*
- * Configuration handler can copy the string to @p buf, the maximum
+ * Configuration handler should copy the string to @p buf, the maximum
* number of bytes it will copy is limited by @p buf_len.
*
* @param name Name/key of the settings item.
@@ -180,10 +167,9 @@
*
* @param buf_len size of buf.
*
- * @return value will be pointer to beginning of the buf,
- * except for string it will pointer to beginning of string source.
+ * @return Positive: Length of copied dat. Negative: -ERCODE
*/
-char *settings_get_value(char *name, char *buf, int buf_len);
+int settings_get_value(char *name, char *buf, int buf_len);
/**
* Call commit for all settings handler. This should apply all
@@ -196,60 +182,20 @@
int settings_commit(char *name);
/**
- * Convenience routine for converting value passed as a string to native
- * data type.
+ * Persistent data extracting routine.
*
- * @param val_str Value of the settings item as string.
- * @param type Type of the value to convert to.
- * @param vp Pointer to variable to fill with the decoded value.
- * @param maxlen the vp buffer size.
+ * This function read and decode data from non volatile storage to user buffer
+ * This function should be used inside set handler in order to read the settings
+ * data from backend storage.
*
- * @return 0 on success, non-zero on failure.
+ * @param[in] value_ctx Data contex provided by the <p>h_set</p> handler.
+ * @param[out] buf Buffer for data read.
+ * @param[in] len Length of <p>buf</p>.
+ *
+ * @retval Negative value on failure. 0 and positive: Length of data loaded to
+ * the <p>buf</p>.
*/
-int settings_value_from_str(char *val_str, enum settings_type type, void *vp,
- int maxlen);
-
-/**
- * Convenience routine for converting byte array passed as a base64
- * encoded string.
- *
- * @param val_str Value of the settings item as string.
- * @param vp Pointer to variable to fill with the decoded value.
- * @param len Size of that variable. On return the number of bytes in the array.
- *
- * @return 0 on success, non-zero on failure.
- */
-int settings_bytes_from_str(char *val_str, void *vp, int *len);
-
-/**
- * Convenience routine for converting native data type to a string.
- *
- * @param type Type of the value to convert from.
- * @param vp Pointer to variable to convert.
- * @param buf Buffer where string value will be stored.
- * @param buf_len Size of the buffer.
- *
- * @return 0 on success, non-zero on failure.
- */
-char *settings_str_from_value(enum settings_type type, void *vp, char *buf,
- int buf_len);
-#define SETTINGS_STR_FROM_BYTES_LEN(len) (((len) * 4 / 3) + 4)
-
-/**
- * Convenience routine for converting byte array into a base64
- * encoded string.
- *
- * @param vp Pointer to variable to convert.
- * @param vp_len Number of bytes to convert.
- * @param buf Buffer where string value will be stored.
- * @param buf_len Size of the buffer.
- *
- * @return 0 on success, non-zero on failure.
- */
-char *settings_str_from_bytes(void *vp, int vp_len, char *buf, int buf_len);
-
-#define SETTINGS_VALUE_SET(str, type, val) \
- settings_value_from_str((str), (type), &(val), sizeof(val))
+int settings_val_read_cb(void *value_ctx, void *buf, size_t len);
/**
* @} settings
diff --git a/subsys/settings/Kconfig b/subsys/settings/Kconfig
index c5172c6..fbf1925 100644
--- a/subsys/settings/Kconfig
+++ b/subsys/settings/Kconfig
@@ -16,6 +16,11 @@
It supports several back-ends to store and load serialized data from
and it can do so atomically for all involved modules.
+# Hidden option to enable encoding length into settings entry
+config SETTINGS_ENCODE_LEN
+ depends on SETTINGS
+ bool
+
choice
prompt "Storage back-end"
default SETTINGS_FCB if FCB
@@ -32,6 +37,7 @@
config SETTINGS_FS
bool "File System"
depends on FILE_SYSTEM
+ select SETTINGS_ENCODE_LEN
help
Use a file system as a settings storage back-end.
endchoice
diff --git a/subsys/settings/include/settings/settings_fcb.h b/subsys/settings/include/settings/settings_fcb.h
index ac7df1f..a1406cd 100644
--- a/subsys/settings/include/settings/settings_fcb.h
+++ b/subsys/settings/include/settings/settings_fcb.h
@@ -22,6 +22,7 @@
extern int settings_fcb_src(struct settings_fcb *cf);
extern int settings_fcb_dst(struct settings_fcb *cf);
+void settings_mount_fcb_backend(struct settings_fcb *cf);
#ifdef __cplusplus
}
diff --git a/subsys/settings/include/settings/settings_file.h b/subsys/settings/include/settings/settings_file.h
index 882719a..92c273c 100644
--- a/subsys/settings/include/settings/settings_file.h
+++ b/subsys/settings/include/settings/settings_file.h
@@ -29,6 +29,8 @@
/* settings saves go to a file */
int settings_file_dst(struct settings_file *cf);
+void settings_mount_fs_backend(struct settings_file *cf);
+
#ifdef __cplusplus
}
#endif
diff --git a/subsys/settings/src/settings.c b/subsys/settings/src/settings.c
index d6c4911..b2ad734 100644
--- a/subsys/settings/src/settings.c
+++ b/subsys/settings/src/settings.c
@@ -8,25 +8,17 @@
#include <string.h>
#include <stdio.h>
#include <stdbool.h>
-
#include <errno.h>
-#include "base64.h"
-
#include "settings/settings.h"
#include "settings_priv.h"
#include <zephyr/types.h>
-/* mbedtls-base64 lib encodes data to null-terminated string */
-#define BASE64_ENCODE_SIZE(in_size) ((((((in_size) - 1) / 3) * 4) + 4) + 1)
-
sys_slist_t settings_handlers;
static u8_t settings_cmd_inited;
void settings_store_init(void);
-static void s64_to_dec(char *ptr, int buf_len, s64_t value, int base);
-static s64_t dec_to_s64(char *p_str, char **e_ptr);
void settings_init(void)
{
@@ -102,223 +94,70 @@
return settings_handler_lookup(name_argv[0]);
}
-int settings_value_from_str(char *val_str, enum settings_type type, void *vp,
- int maxlen)
+int settings_set_value_priv(char *name, void *val_read_cb_ctx, off_t off,
+ u8_t is_runtime)
{
- s32_t val;
- s64_t val64;
- char *eptr;
+ int name_argc;
+ char *name_argv[SETTINGS_MAX_DIR_DEPTH];
+ struct settings_handler *ch;
+ struct read_value_cb_ctx value_ctx;
- if (!val_str) {
- goto err;
+ ch = settings_parse_and_lookup(name, &name_argc, name_argv);
+ if (!ch) {
+ return -EINVAL;
}
- switch (type) {
- case SETTINGS_INT8:
- case SETTINGS_INT16:
- case SETTINGS_INT32:
- case SETTINGS_BOOL:
- val = strtol(val_str, &eptr, 0);
- if (*eptr != '\0') {
- goto err;
- }
- if (type == SETTINGS_BOOL) {
- if (val < 0 || val > 1) {
- goto err;
- }
- *(bool *)vp = val;
- } else if (type == SETTINGS_INT8) {
- if (val < INT8_MIN || val > UINT8_MAX) {
- goto err;
- }
- *(int8_t *)vp = val;
- } else if (type == SETTINGS_INT16) {
- if (val < INT16_MIN || val > UINT16_MAX) {
- goto err;
- }
- *(int16_t *)vp = val;
- } else if (type == SETTINGS_INT32) {
- *(s32_t *)vp = val;
- }
- break;
- case SETTINGS_INT64:
- val64 = dec_to_s64(val_str, &eptr);
- if (*eptr != '\0') {
- goto err;
- }
- *(s64_t *)vp = val64;
- break;
- case SETTINGS_STRING:
- val = strlen(val_str);
- if (val + 1 > maxlen) {
- goto err;
- }
- strcpy(vp, val_str);
- break;
- default:
- goto err;
- }
- return 0;
-err:
- return -EINVAL;
+
+ value_ctx.read_cb_ctx = val_read_cb_ctx;
+ value_ctx.off = off;
+ value_ctx.runtime = is_runtime;
+
+ return ch->h_set(name_argc - 1, &name_argv[1], (void *)&value_ctx);
}
-int settings_bytes_from_str(char *val_str, void *vp, int *len)
+/* API */
+int settings_set_value(char *name, void *val_str, size_t len)
{
- int err;
+ struct runtime_value_ctx rt_ctx;
+
+ rt_ctx.p_value = val_str;
+ rt_ctx.size = len;
+
+ return settings_set_value_priv(name, &rt_ctx, 0, 1);
+}
+
+/* API */
+int settings_val_read_cb(void *value_ctx, void *buf, size_t len)
+{
+ struct read_value_cb_ctx *value_context = value_ctx;
+ size_t len_read;
int rc;
+ struct runtime_value_ctx *rt_ctx;
- err = base64_decode(vp, *len, &rc, val_str, strlen(val_str));
-
- if (err) {
- return -1;
- }
-
- *len = rc;
- return 0;
-}
-
-char *settings_str_from_value(enum settings_type type, void *vp, char *buf,
- int buf_len)
-{
- s32_t val;
-
- if (type == SETTINGS_STRING) {
- return vp;
- }
- switch (type) {
- case SETTINGS_INT8:
- case SETTINGS_INT16:
- case SETTINGS_INT32:
- case SETTINGS_BOOL:
- if (type == SETTINGS_BOOL) {
- val = *(bool *)vp;
- } else if (type == SETTINGS_INT8) {
- val = *(int8_t *)vp;
- } else if (type == SETTINGS_INT16) {
- val = *(int16_t *)vp;
- } else {
- val = *(s32_t *)vp;
- }
- snprintf(buf, buf_len, "%ld", (long)val);
- return buf;
- case SETTINGS_INT64:
- s64_to_dec(buf, buf_len, *(s64_t *)vp, 10);
- return buf;
- default:
- return NULL;
- }
-}
-
-static void u64_to_dec(char *ptr, int buf_len, u64_t value, int base)
-{
- u64_t t = 0U, res = 0U;
- u64_t tmp = value;
- int count = 0;
-
- if (ptr == NULL) {
- return;
- }
-
- if (tmp == 0) {
- count++;
- }
-
- while (tmp > 0) {
- tmp = tmp/base;
- count++;
- }
-
- ptr += count;
-
- *ptr = '\0';
-
- do {
- res = value - base * (t = value / base);
- if (res < 10) {
- *--ptr = '0' + res;
- } else if ((res >= 10) && (res < 16)) {
- *--ptr = 'A' - 10 + res;
- }
- value = t;
- } while (value != 0);
-}
-
-static void s64_to_dec(char *ptr, int buf_len, s64_t value, int base)
-{
- u64_t val64;
-
- if (ptr == NULL || buf_len < 1) {
- return;
- }
-
- if (value < 0) {
- *ptr = '-';
- ptr++;
- buf_len--;
- val64 = value * (-1);
+ if (value_context->runtime) {
+ rt_ctx = value_context->read_cb_ctx;
+ len_read = min(len, rt_ctx->size);
+ memcpy(buf, rt_ctx->p_value, len_read);
+ return len_read;
} else {
- val64 = value;
+ rc = settings_line_val_read(value_context->off, 0, buf, len,
+ &len_read,
+ value_context->read_cb_ctx);
}
- u64_to_dec(ptr, buf_len, val64, base);
+ if (rc == 0) {
+ return len_read;
+ }
+
+ return rc;
}
-static s64_t dec_to_s64(char *p_str, char **e_ptr)
-{
- u64_t val = 0U, prev_val = 0U;
- bool neg = false;
- int digit;
-
- if (*p_str == '-') {
- neg = true;
- p_str++;
- } else if (*p_str == '+') {
- p_str++;
- }
-
- while (1) {
- if (*p_str >= '0' && *p_str <= '9') {
- digit = *p_str - '0';
- } else {
- break;
- }
-
- val *= 10;
- val += digit;
-
- /* this is only a fuse */
- if (val < prev_val) {
- break;
- }
-
- prev_val = val;
- p_str++;
- }
-
- if (e_ptr != 0)
- *e_ptr = p_str;
-
- if (neg) {
- return -val;
- } else {
- return val;
- }
-}
-
-char *settings_str_from_bytes(void *vp, int vp_len, char *buf, int buf_len)
-{
- if (BASE64_ENCODE_SIZE(vp_len) > buf_len) {
- return NULL;
- }
-
- size_t enc_len;
-
- base64_encode(buf, buf_len, &enc_len, vp, vp_len);
-
- return buf;
-}
-
-int settings_set_value(char *name, char *val_str)
+/*
+ * Get value in printable string form. If value is not string, the value
+ * will be filled in *buf.
+ * Return value will be pointer to beginning of that buffer,
+ * except for string it will pointer to beginning of string.
+ */
+int settings_get_value(char *name, char *buf, int buf_len)
{
int name_argc;
char *name_argv[SETTINGS_MAX_DIR_DEPTH];
@@ -329,28 +168,8 @@
return -EINVAL;
}
- return ch->h_set(name_argc - 1, &name_argv[1], val_str);
-}
-
-/*
- * Get value in printable string form. If value is not string, the value
- * will be filled in *buf.
- * Return value will be pointer to beginning of that buffer,
- * except for string it will pointer to beginning of string.
- */
-char *settings_get_value(char *name, char *buf, int buf_len)
-{
- int name_argc;
- char *name_argv[SETTINGS_MAX_DIR_DEPTH];
- struct settings_handler *ch;
-
- ch = settings_parse_and_lookup(name, &name_argc, name_argv);
- if (!ch) {
- return NULL;
- }
-
if (!ch->h_get) {
- return NULL;
+ return -EINVAL;
}
return ch->h_get(name_argc - 1, &name_argv[1], buf, buf_len);
}
diff --git a/subsys/settings/src/settings_fcb.c b/subsys/settings/src/settings_fcb.c
index 890d587..4b0dcc0 100644
--- a/subsys/settings/src/settings_fcb.c
+++ b/subsys/settings/src/settings_fcb.c
@@ -23,7 +23,7 @@
static int settings_fcb_load(struct settings_store *cs, load_cb cb,
void *cb_arg);
static int settings_fcb_save(struct settings_store *cs, const char *name,
- const char *value);
+ const char *value, size_t val_len);
static struct settings_store_itf settings_fcb_itf = {
.csi_load = settings_fcb_load,
@@ -79,33 +79,23 @@
static int settings_fcb_load_cb(struct fcb_entry_ctx *entry_ctx, void *arg)
{
struct settings_fcb_load_cb_arg *argp;
- char buf[SETTINGS_MAX_NAME_LEN + SETTINGS_MAX_VAL_LEN +
- SETTINGS_EXTRA_LEN];
- char *name_str;
- char *val_str;
+ char buf[SETTINGS_MAX_NAME_LEN + SETTINGS_EXTRA_LEN + 1];
int rc;
- int len;
argp = (struct settings_fcb_load_cb_arg *)arg;
- len = entry_ctx->loc.fe_data_len;
- if (len >= sizeof(buf)) {
- len = sizeof(buf) - 1;
- }
+ size_t len_read;
- rc = flash_area_read(entry_ctx->fap,
- FCB_ENTRY_FA_DATA_OFF(entry_ctx->loc), buf, len);
-
+ rc = settings_line_name_read(buf, sizeof(buf), &len_read,
+ (void *)&entry_ctx->loc);
if (rc) {
return 0;
}
- buf[len] = '\0';
+ buf[len_read] = '\0';
- rc = settings_line_parse(buf, &name_str, &val_str);
- if (rc) {
- return 0;
- }
- argp->cb(name_str, val_str, argp->cb_arg);
+ /*name, val-read_cb-ctx, val-off*/
+ /* take into account '=' separator after the name */
+ argp->cb(buf, (void *)&entry_ctx->loc, len_read + 1, argp->cb_arg);
return 0;
}
@@ -125,64 +115,79 @@
return 0;
}
-static int settings_fcb_var_read(struct fcb_entry_ctx *entry_ctx, char *buf,
- char **name, char **val)
+static int read_handler(void *ctx, off_t off, char *buf, size_t *len)
{
- int rc;
+ struct fcb_entry_ctx *entry_ctx = ctx;
- rc = flash_area_read(entry_ctx->fap,
- FCB_ENTRY_FA_DATA_OFF(entry_ctx->loc), buf,
- entry_ctx->loc.fe_data_len);
- if (rc) {
- return rc;
+ if (off >= entry_ctx->loc.fe_data_len) {
+ return -EINVAL;
}
- buf[entry_ctx->loc.fe_data_len] = '\0';
- rc = settings_line_parse(buf, name, val);
- return rc;
+
+ if ((off + *len) > entry_ctx->loc.fe_data_len) {
+ *len = entry_ctx->loc.fe_data_len - off;
+ }
+
+ return flash_area_read(entry_ctx->fap,
+ FCB_ENTRY_FA_DATA_OFF(entry_ctx->loc) + off, buf,
+ *len);
}
static void settings_fcb_compress(struct settings_fcb *cf)
{
int rc;
- char buf1[SETTINGS_MAX_NAME_LEN + SETTINGS_MAX_VAL_LEN +
- SETTINGS_EXTRA_LEN];
- char buf2[SETTINGS_MAX_NAME_LEN + SETTINGS_MAX_VAL_LEN +
- SETTINGS_EXTRA_LEN];
struct fcb_entry_ctx loc1;
struct fcb_entry_ctx loc2;
- char *name1, *val1;
- char *name2, *val2;
+ char name1[SETTINGS_MAX_NAME_LEN + SETTINGS_EXTRA_LEN];
+ char name2[SETTINGS_MAX_NAME_LEN + SETTINGS_EXTRA_LEN];
int copy;
+ u8_t rbs;
rc = fcb_append_to_scratch(&cf->cf_fcb);
if (rc) {
return; /* XXX */
}
+ rbs = flash_area_align(cf->cf_fcb.fap);
+
loc1.fap = cf->cf_fcb.fap;
loc1.loc.fe_sector = NULL;
loc1.loc.fe_elem_off = 0;
+
while (fcb_getnext(&cf->cf_fcb, &loc1.loc) == 0) {
if (loc1.loc.fe_sector != cf->cf_fcb.f_oldest) {
break;
}
- rc = settings_fcb_var_read(&loc1, buf1, &name1, &val1);
+
+ off_t val1_off;
+
+ rc = settings_line_name_read(name1, sizeof(name1), &val1_off,
+ &loc1);
if (rc) {
continue;
}
- if (!val1) {
- /* No sense to copy empty entry from the oldest sector*/
+
+ if (val1_off + 1 == loc1.loc.fe_data_len) {
+ /* Lack of a value so the record is a deletion-record */
+ /* No sense to copy empty entry from */
+ /* the oldest sector */
continue;
}
+
loc2 = loc1;
copy = 1;
+
while (fcb_getnext(&cf->cf_fcb, &loc2.loc) == 0) {
- rc = settings_fcb_var_read(&loc2, buf2, &name2, &val2);
+ off_t val2_off;
+
+ rc = settings_line_name_read(name2, sizeof(name2),
+ &val2_off, &loc2);
if (rc) {
continue;
}
- if (!strcmp(name1, name2)) {
+
+ if ((val1_off == val2_off) &&
+ !memcmp(name1, name2, val1_off)) {
copy = 0;
break;
}
@@ -194,17 +199,13 @@
/*
* Can't find one. Must copy.
*/
- rc = flash_area_read(loc1.fap, FCB_ENTRY_FA_DATA_OFF(loc1.loc),
- buf1, loc1.loc.fe_data_len);
- if (rc) {
- continue;
- }
rc = fcb_append(&cf->cf_fcb, loc1.loc.fe_data_len, &loc2.loc);
if (rc) {
continue;
}
- rc = flash_area_write(loc2.fap, FCB_ENTRY_FA_DATA_OFF(loc2.loc),
- buf1, loc1.loc.fe_data_len);
+
+ rc = settings_entry_copy(&loc2, 0, &loc1, 0,
+ loc1.loc.fe_data_len);
if (rc) {
continue;
}
@@ -216,14 +217,35 @@
__ASSERT(rc == 0, "Failed to fcb rotate.\n");
}
-static int settings_fcb_append(struct settings_fcb *cf, char *buf, int len)
+static int write_handler(void *ctx, off_t off, char const *buf, size_t len)
{
+ struct fcb_entry_ctx *entry_ctx = ctx;
+
+ return flash_area_write(entry_ctx->fap,
+ FCB_ENTRY_FA_DATA_OFF(entry_ctx->loc) + off,
+ buf, len);
+}
+
+/* ::csi_save implementation */
+static int settings_fcb_save(struct settings_store *cs, const char *name,
+ const char *value, size_t val_len)
+{
+ struct settings_fcb *cf = (struct settings_fcb *)cs;
+ struct fcb_entry_ctx loc;
+ int len;
int rc;
int i;
- struct fcb_entry loc;
+ u8_t wbs;
- for (i = 0; i < 10; i++) {
- rc = fcb_append(&cf->cf_fcb, len, &loc);
+ if (!name) {
+ return -EINVAL;
+ }
+
+ wbs = flash_area_align(cf->cf_fcb.fap);
+ len = settings_line_len_calc(name, val_len);
+
+ for (i = 0; i < CONFIG_SETTINGS_FCB_NUM_AREAS; i++) {
+ rc = fcb_append(&cf->cf_fcb, len, &loc.loc);
if (rc != FCB_ERR_NOSPACE) {
break;
}
@@ -233,29 +255,24 @@
return -EINVAL;
}
- rc = flash_area_write(cf->cf_fcb.fap, FCB_ENTRY_FA_DATA_OFF(loc),
- buf, len);
- if (rc) {
- return -EINVAL;
+ loc.fap = cf->cf_fcb.fap;
+
+ rc = settings_line_write(name, value, val_len, 0, (void *)&loc);
+
+ if (rc != -EIO) {
+ i = fcb_append_finish(&cf->cf_fcb, &loc.loc);
+ if (!rc) {
+ rc = i;
+ }
}
- return fcb_append_finish(&cf->cf_fcb, &loc);
+ return rc;
}
-static int settings_fcb_save(struct settings_store *cs, const char *name,
- const char *value)
+void settings_mount_fcb_backend(struct settings_fcb *cf)
{
- struct settings_fcb *cf = (struct settings_fcb *)cs;
- char buf[SETTINGS_MAX_NAME_LEN + SETTINGS_MAX_VAL_LEN +
- SETTINGS_EXTRA_LEN];
- int len;
+ u8_t rbs;
- if (!name) {
- return -EINVAL;
- }
+ rbs = cf->cf_fcb.f_align;
- len = settings_line_make(buf, sizeof(buf), name, value);
- if (len < 0 || len + 2 > sizeof(buf)) {
- return -EINVAL;
- }
- return settings_fcb_append(cf, buf, len);
+ settings_line_io_init(read_handler, write_handler, rbs);
}
diff --git a/subsys/settings/src/settings_file.c b/subsys/settings/src/settings_file.c
index 687773b..bc88957 100644
--- a/subsys/settings/src/settings_file.c
+++ b/subsys/settings/src/settings_file.c
@@ -18,7 +18,7 @@
static int settings_file_load(struct settings_store *cs, load_cb cb,
void *cb_arg);
static int settings_file_save(struct settings_store *cs, const char *name,
- const char *value);
+ const char *value, size_t val_len);
static struct settings_store_itf settings_file_itf = {
.csi_load = settings_file_load,
@@ -50,40 +50,6 @@
return 0;
}
-int settings_getnext_line(struct fs_file_t *file, char *buf, int blen,
- off_t *loc)
-{
- int rc;
- char *end;
-
- rc = fs_seek(file, *loc, FS_SEEK_SET);
- if (rc < 0) {
- *loc = 0;
- return -1;
- }
-
- rc = fs_read(file, buf, blen);
- if (rc <= 0) {
- *loc = 0;
- return -1;
- }
-
- if (rc == blen) {
- rc--;
- }
- buf[rc] = '\0';
-
- end = strchr(buf, '\n');
- if (end) {
- *end = '\0';
- } else {
- end = strchr(buf, '\0');
- }
- blen = end - buf;
- *loc += (blen + 1);
- return blen;
-}
-
/*
* Called to load configuration items. cb must be called for every configuration
* item found.
@@ -92,37 +58,53 @@
void *cb_arg)
{
struct settings_file *cf = (struct settings_file *)cs;
+ char buf[SETTINGS_MAX_NAME_LEN + SETTINGS_EXTRA_LEN + 1];
+ struct fs_dirent file_info;
struct fs_file_t file;
- off_t loc;
- char tmpbuf[SETTINGS_MAX_NAME_LEN + SETTINGS_MAX_VAL_LEN +
- SETTINGS_EXTRA_LEN];
- char *name_str;
- char *val_str;
- int rc;
+ size_t len_read;
int lines;
+ int rc;
+
+
+ struct line_entry_ctx entry_ctx = {
+ .stor_ctx = (void *)&file,
+ .seek = 0,
+ .len = 0 /* unknown length */
+ };
+
+ lines = 0;
+
+ rc = fs_stat(cf->cf_name, &file_info);
+ if (rc) {
+ return rc;
+ }
rc = fs_open(&file, cf->cf_name);
if (rc != 0) {
return -EINVAL;
}
- loc = 0;
- lines = 0;
while (1) {
- rc = settings_getnext_line(&file, tmpbuf, sizeof(tmpbuf), &loc);
- if (loc == 0) {
+ rc = settings_next_line_ctx(&entry_ctx);
+
+ if (rc || entry_ctx.len == 0) {
break;
}
- if (rc < 0) {
- continue;
+
+ rc = settings_line_name_read(buf, sizeof(buf), &len_read,
+ (void *)&entry_ctx);
+
+ if (rc || len_read == 0) {
+ break;
}
- rc = settings_line_parse(tmpbuf, &name_str, &val_str);
- if (rc != 0) {
- continue;
- }
+ buf[len_read] = '\0';
+
+ /*name, val-read_cb-ctx, val-off*/
+ /* take into account '=' separator after the name */
+ cb(buf, (void *)&entry_ctx, len_read + 1, cb_arg);
lines++;
- cb(name_str, val_str, cb_arg);
}
+
rc = fs_close(&file);
cf->cf_lines = lines;
@@ -161,58 +143,101 @@
return fs_open(zfp, file_name);
}
+
/*
* Try to compress configuration file by keeping unique names only.
*/
-void settings_file_compress(struct settings_file *cf)
+int settings_file_save_and_compress(struct settings_file *cf, const char *name,
+ const char *value, size_t val_len)
{
- int rc;
+ int rc, rc2;
struct fs_file_t rf;
struct fs_file_t wf;
char tmp_file[SETTINGS_FILE_NAME_MAX];
- char buf1[SETTINGS_MAX_NAME_LEN + SETTINGS_MAX_VAL_LEN +
- SETTINGS_EXTRA_LEN];
- char buf2[SETTINGS_MAX_NAME_LEN + SETTINGS_MAX_VAL_LEN +
- SETTINGS_EXTRA_LEN];
- off_t loc1, loc2;
- char *name1, *val1;
- char *name2, *val2;
+ char name1[SETTINGS_MAX_NAME_LEN + SETTINGS_EXTRA_LEN];
+ char name2[SETTINGS_MAX_NAME_LEN + SETTINGS_EXTRA_LEN];
+ struct line_entry_ctx loc1 = {
+ .stor_ctx = &rf,
+ .seek = 0,
+ .len = 0 /* unknown length */
+ };
+
+ struct line_entry_ctx loc2;
+
+ struct line_entry_ctx loc3 = {
+ .stor_ctx = &wf
+ };
+
int copy;
- int len, len2;
int lines;
+ size_t new_name_len;
+ off_t val1_off;
if (fs_open(&rf, cf->cf_name) != 0) {
- return;
+ return -ENOEXEC;
}
settings_tmpfile(tmp_file, cf->cf_name, ".cmp");
if (settings_file_create_or_replace(&wf, tmp_file)) {
fs_close(&rf);
- return;
+ return -ENOEXEC;
}
- loc1 = 0;
lines = 0;
+ new_name_len = strlen(name);
+
while (1) {
- len = settings_getnext_line(&rf, buf1, sizeof(buf1), &loc1);
- if (loc1 == 0 || len < 0) {
+ rc = settings_next_line_ctx(&loc1);
+
+ if (rc || loc1.len == 0) {
+ /* try to amend new value to the commpresed file */
break;
}
- rc = settings_line_parse(buf1, &name1, &val1);
+
+ rc = settings_line_name_read(name1, sizeof(name1), &val1_off,
+ &loc1);
if (rc) {
+ /* try to process next line */
continue;
}
+
+ if (val1_off + 1 == loc1.len) {
+ /* Lack of a value so the record is a deletion-record */
+ /* No sense to copy empty entry from */
+ /* the oldest sector */
+ continue;
+ }
+
+ /* avoid copping value which will be overwritten by new value*/
+ if ((val1_off == new_name_len) &&
+ !memcmp(name1, name, val1_off)) {
+ continue;
+ }
+
loc2 = loc1;
+
copy = 1;
- while ((len2 = settings_getnext_line(&rf, buf2, sizeof(buf2),
- &loc2)) > 0) {
- rc = settings_line_parse(buf2, &name2, &val2);
+ while (1) {
+ off_t val2_off;
+
+ rc = settings_next_line_ctx(&loc2);
+
+ if (rc || loc2.len == 0) {
+ /* try to amend new value to */
+ /* the commpresed file */
+ break;
+ }
+
+ rc = settings_line_name_read(name2, sizeof(name2),
+ &val2_off, &loc2);
if (rc) {
+ /* try to process next line */
continue;
}
- if (!strcmp(name1, name2)) {
- copy = 0;
+ if ((val1_off == val2_off) &&
+ !memcmp(name1, name2, val1_off)) {
+ copy = 0; /* newer version doesn't exist */
break;
}
}
@@ -220,45 +245,58 @@
continue;
}
- /*
- * Can't find one. Must copy.
- */
- len = settings_line_make(buf2, sizeof(buf2), name1, val1);
- if (len < 0 || len + 2 > sizeof(buf2)) {
- continue;
+ loc2 = loc1;
+ loc2.len += 2;
+ loc2.seek -= 2;
+ rc = settings_entry_copy(&loc3, 0, &loc2, 0, loc2.len);
+ if (rc) {
+ /* compressed file might be corrupted */
+ goto end_rolback;
}
- buf2[len++] = '\n';
- if (fs_write(&wf, buf2, len) != len) {
- ARG_UNUSED(fs_close(&rf));
- ARG_UNUSED(fs_close(&wf));
- return;
- }
- lines++;
+
+ lines++;
}
- len = fs_close(&wf);
- len2 = fs_close(&rf);
- if (len == 0 && len2 == 0 && fs_unlink(cf->cf_name) == 0) {
- ARG_UNUSED(fs_rename(tmp_file, cf->cf_name));
- cf->cf_lines = lines;
+ /* at last store the new value */
+ rc = settings_line_write(name, value, val_len, 0, &loc3);
+ if (rc) {
+ /* compressed file might be corrupted */
+ goto end_rolback;
+ }
+
+ rc = fs_close(&wf);
+ rc2 = fs_close(&rf);
+ if (rc == 0 && rc2 == 0 && fs_unlink(cf->cf_name) == 0) {
+ if (fs_rename(tmp_file, cf->cf_name)) {
+ return -ENOENT;
+ }
+ cf->cf_lines = lines + 1;
+ } else {
+ rc = -EIO;
+ }
/*
* XXX at settings_file_load(), look for .cmp if actual file does not
* exist.
*/
+ return 0;
+end_rolback:
+ (void)fs_close(&wf);
+ if (fs_close(&rf) == 0) {
+ (void)fs_unlink(tmp_file);
}
+ return -EIO;
+
}
/*
* Called to save configuration.
*/
static int settings_file_save(struct settings_store *cs, const char *name,
- const char *value)
+ const char *value, size_t val_len)
{
struct settings_file *cf = (struct settings_file *)cs;
+ struct line_entry_ctx entry_ctx;
struct fs_file_t file;
- char buf[SETTINGS_MAX_NAME_LEN + SETTINGS_MAX_VAL_LEN +
- SETTINGS_EXTRA_LEN];
- int len;
int rc2;
int rc;
@@ -271,13 +309,9 @@
* Compress before config file size exceeds
* the max number of lines.
*/
- settings_file_compress(cf);
+ return settings_file_save_and_compress(cf, name, value,
+ val_len);
}
- len = settings_line_make(buf, sizeof(buf), name, value);
- if (len < 0 || len + 2 > sizeof(buf)) {
- return -EINVAL;
- }
- buf[len++] = '\n';
/*
* Open the file to add this one value.
@@ -286,8 +320,10 @@
if (rc == 0) {
rc = fs_seek(&file, 0, FS_SEEK_END);
if (rc == 0) {
- rc2 = fs_write(&file, buf, len);
- if (rc2 == len) {
+ entry_ctx.stor_ctx = &file;
+ rc2 = settings_line_write(name, value, val_len, 0,
+ (void *)&entry_ctx);
+ if (rc2 == 0) {
cf->cf_lines++;
}
}
@@ -300,3 +336,63 @@
return rc;
}
+
+static int read_handler(void *ctx, off_t off, char *buf, size_t *len)
+{
+ struct line_entry_ctx *entry_ctx = ctx;
+ struct fs_file_t *file = entry_ctx->stor_ctx;
+ ssize_t r_len;
+ int rc;
+
+ /* 0 is reserved for reding the length-field only */
+ if (entry_ctx->len != 0) {
+ if (off >= entry_ctx->len) {
+ return -EINVAL;
+ }
+
+ if ((off + *len) > entry_ctx->len) {
+ *len = entry_ctx->len - off;
+ }
+ }
+
+ rc = fs_seek(file, entry_ctx->seek + off, FS_SEEK_SET);
+ if (rc) {
+ goto end;
+ }
+
+ r_len = fs_read(file, buf, *len);
+
+ if (r_len >= 0) {
+ *len = r_len;
+ rc = 0;
+ } else {
+ rc = r_len;
+ }
+end:
+ return rc;
+}
+
+static int write_handler(void *ctx, off_t off, char const *buf, size_t len)
+{
+ struct line_entry_ctx *entry_ctx = ctx;
+ struct fs_file_t *file = entry_ctx->stor_ctx;
+ int rc;
+
+ /* append to file only */
+ rc = fs_seek(file, 0, FS_SEEK_END);
+
+ if (rc == 0) {
+ rc = fs_write(file, buf, len);
+
+ if (rc > 0) {
+ rc = 0;
+ }
+ }
+
+ return rc;
+}
+
+void settings_mount_fs_backend(struct settings_file *cf)
+{
+ settings_line_io_init(read_handler, write_handler, 1);
+}
diff --git a/subsys/settings/src/settings_init.c b/subsys/settings/src/settings_init.c
index 479551a..58bca08 100644
--- a/subsys/settings/src/settings_init.c
+++ b/subsys/settings/src/settings_init.c
@@ -40,6 +40,8 @@
if (rc) {
k_panic();
}
+
+ settings_mount_fs_backend(&config_init_settings_file);
}
#elif defined(CONFIG_SETTINGS_FCB)
@@ -93,6 +95,8 @@
if (rc != 0) {
k_panic();
}
+
+ settings_mount_fcb_backend(&config_init_settings_fcb);
}
#endif
diff --git a/subsys/settings/src/settings_line.c b/subsys/settings/src/settings_line.c
index fb1f91a..36ebc26 100644
--- a/subsys/settings/src/settings_line.c
+++ b/subsys/settings/src/settings_line.c
@@ -10,6 +10,8 @@
#include "settings/settings.h"
#include "settings_priv.h"
+#include "base64.h"
+#include "misc/printk.h"
int settings_line_parse(char *buf, char **namep, char **valp)
{
@@ -87,3 +89,360 @@
return off;
}
+
+struct settings_io_cb_s {
+ int (*read_cb)(void *ctx, off_t off, char *buf, size_t *len);
+ int (*write_cb)(void *ctx, off_t off, char const *buf, size_t len);
+ u8_t rwbs;
+} settings_io_cb;
+
+#define MAX_ENC_BLOCK_SIZE 4
+
+int settings_line_write(const char *name, const char *value, size_t val_len,
+ off_t w_loc, void *cb_arg)
+{
+ size_t w_size, rem, add, enc_len;
+
+ /* minimal buffer for encoding base64 + EOL*/
+ char enc_buf[MAX_ENC_BLOCK_SIZE + 1];
+
+ char *p_enc = enc_buf;
+ bool done;
+ char w_buf[16]; /* write buff, must be aligned either to minimal */
+ /* base64 encoding size and write-block-size */
+ int rc;
+ u8_t wbs = settings_io_cb.rwbs;
+#ifdef CONFIG_SETTINGS_ENCODE_LEN
+ u16_t len_field;
+#endif
+
+ rem = strlen(name);
+
+#ifdef CONFIG_SETTINGS_ENCODE_LEN
+ len_field = settings_line_len_calc(name, val_len);
+ memcpy(w_buf, &len_field, sizeof(len_field));
+ w_size = 0;
+
+
+ add = sizeof(len_field) % wbs;
+ if (add) {
+ w_size = wbs - add;
+ if (rem < w_size) {
+ w_size = rem;
+ }
+
+ memcpy(w_buf + sizeof(len_field), name, w_size);
+ name += w_size;
+ rem -= w_size;
+ }
+
+ w_size += sizeof(len_field);
+ if (w_size % wbs == 0) {
+ rc = settings_io_cb.write_cb(cb_arg, w_loc, w_buf, w_size);
+ if (rc) {
+ return -EIO;
+ }
+ }
+ /* The Alternative to condition above mean that `rem == 0` as `name` */
+ /* must have been consumed */
+#endif
+ w_size = rem - rem % wbs;
+ rem %= wbs;
+
+ rc = settings_io_cb.write_cb(cb_arg, w_loc, name, w_size);
+ w_loc += w_size;
+ name += w_size;
+ w_size = rem;
+
+ if (rem) {
+ memcpy(w_buf, name, rem);
+ }
+
+ w_buf[rem] = '=';
+ w_size++;
+
+ rem = val_len;
+ enc_len = 0;
+ done = false;
+
+ while (1) {
+ while (w_size < sizeof(w_buf)) {
+ if (enc_len) {
+ add = min(enc_len, sizeof(w_buf) - w_size);
+ memcpy(&w_buf[w_size], p_enc, add);
+ enc_len -= add;
+ w_size += add;
+ p_enc += add;
+ } else {
+ if (rem) {
+ add = min(rem, MAX_ENC_BLOCK_SIZE/4*3);
+ rc = base64_encode(enc_buf, sizeof(enc_buf), &enc_len, value, add);
+ if (rc) {
+ return -EINVAL;
+ }
+ value += add;
+ rem -= add;
+ p_enc = enc_buf;
+ } else {
+ add = (w_size) % wbs;
+ if (add) {
+ add = wbs - add;
+ memset(&w_buf[w_size], '\0',
+ add);
+ w_size += add;
+ }
+ done = true;
+ break;
+ }
+ }
+ }
+
+ rc = settings_io_cb.write_cb(cb_arg, w_loc, w_buf, w_size);
+ if (rc) {
+ return -EIO;
+ }
+
+ if (done) {
+ break;
+ }
+ w_loc += w_size;
+ w_size = 0;
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_SETTINGS_ENCODE_LEN
+int settings_next_line_ctx(struct line_entry_ctx *entry_ctx)
+{
+ size_t len_read;
+ u16_t readout;
+ int rc;
+
+ entry_ctx->seek += entry_ctx->len; /* to begin of nex line */
+
+ entry_ctx->len = 0; /* ask read handler to ignore len */
+
+ rc = settings_line_raw_read(0, (char *)&readout, sizeof(readout),
+ &len_read, entry_ctx);
+ if (rc == 0) {
+ if (len_read != sizeof(readout)) {
+ if (len_read != 0) {
+ rc = -ESPIPE;
+ }
+ } else {
+ entry_ctx->seek += sizeof(readout);
+ entry_ctx->len = readout;
+ }
+ }
+
+ return rc;
+}
+#endif
+
+int settings_line_len_calc(const char *name, size_t val_len)
+{
+ int len, mod;
+
+ /* <enc(value)> */
+ len = val_len/3*4 + ((val_len%3) ? 4 : 0);
+ /* <name>=<enc(value)> */
+ len += strlen(name) + 1;
+ mod = len % settings_io_cb.rwbs;
+ if (mod) {
+ /*additional \0 for meet flash alignment */
+ len += settings_io_cb.rwbs - mod;
+ }
+ return len;
+}
+
+
+/**
+ * Read RAW settings line entry data until a char from the storage.
+ *
+ * @param seek offset form the line beginning.
+ * @param[out] out buffer for name
+ * @param[in] len_req size of <p>out</p> buffer
+ * @param[out] len_read length of read name
+ * @param[in] until_char pointer on char value until which all line data will
+ * be read. If Null entire data will be read.
+ * @param[in] cb_arg settings line storage context expected by the
+ * <p>read_cb</p> implementation
+ *
+ * @retval 0 on success,
+ * -ERCODE on storage errors
+ */
+static int settings_line_raw_read_until(off_t seek, char *out, size_t len_req,
+ size_t *len_read, char const *until_char,
+ void *cb_arg)
+{
+ size_t rem_size, len;
+ char temp_buf[16]; /* buffer for fit read-block-size requirements */
+ size_t exp_size, read_size;
+ u8_t rbs = settings_io_cb.rwbs;
+ off_t off;
+ int rc;
+
+ rem_size = len_req;
+
+ while (rem_size) {
+ off = seek / rbs * rbs;
+
+ read_size = sizeof(temp_buf);
+ exp_size = read_size;
+
+ rc = settings_io_cb.read_cb(cb_arg, off, temp_buf, &read_size);
+ if (rc) {
+ return -EIO;
+ }
+
+ off = seek - off;
+ len = read_size - off;
+ len = min(rem_size, len);
+
+ if (until_char != NULL) {
+ char *pend;
+ pend = memchr(&temp_buf[off], *until_char, len);
+ if (pend != NULL) {
+ len = pend - &temp_buf[off];
+ rc = 1; /* will cause loop expiration */
+ }
+ }
+
+ memcpy(out, &temp_buf[off], len);
+
+ rem_size -= len;
+
+ if (exp_size > read_size || rc) {
+ break;
+ }
+
+ out += len;
+ seek += len;
+ }
+
+ *len_read = len_req - rem_size;
+
+ if (until_char != NULL) {
+ return (rc) ? 0 : 1;
+ }
+
+ return 0;
+}
+
+int settings_line_raw_read(off_t seek, char *out, size_t len_req,
+ size_t *len_read, void *cb_arg)
+{
+ return settings_line_raw_read_until(seek, out, len_req, len_read,
+ NULL, cb_arg);
+}
+
+/* off from value begin */
+int settings_line_val_read(off_t val_off, off_t off, char *out, size_t len_req,
+ size_t *len_read, void *cb_arg)
+{
+ char enc_buf[16 + 1];
+ char dec_buf[sizeof(enc_buf)/4 * 3 + 1];
+ size_t rem_size, read_size, exp_size, clen, olen;
+ off_t seek_begin, off_begin;
+ int rc;
+
+
+ rem_size = len_req;
+
+ while (rem_size) {
+ seek_begin = off / 3 * 4;
+ off_begin = seek_begin / 4 * 3;
+
+ read_size = rem_size / 3 * 4;
+ read_size += (rem_size % 3 != 0 || off_begin != off) ? 4 : 0;
+
+ read_size = min(read_size, sizeof(enc_buf) - 1);
+ exp_size = read_size;
+
+ rc = settings_line_raw_read(val_off + seek_begin, enc_buf,
+ read_size, &read_size, cb_arg);
+ if (rc) {
+ return rc;
+ }
+
+ enc_buf[read_size] = 0; /* breaking guaranted */
+ read_size = strlen(enc_buf);
+
+ if (read_size == 0 || read_size % 4) {
+ /* unexpected use case - a NULL value or an encoding */
+ /* problem */
+ return -EINVAL;
+ }
+
+ rc = base64_decode(dec_buf, sizeof(dec_buf), &olen, enc_buf,
+ read_size);
+ dec_buf[olen] = 0;
+
+ clen = min(olen + off_begin - off, rem_size);
+
+ memcpy(out, &dec_buf[off - off_begin], clen);
+ rem_size -= clen;
+
+ if (exp_size > read_size || olen < read_size/4*3) {
+ break;
+ }
+
+ out += clen;
+ off += clen;
+ }
+
+ *len_read = len_req - rem_size;
+
+ return 0;
+}
+
+
+int settings_line_name_read(char *out, size_t len_req, size_t *len_read,
+ void *cb_arg)
+{
+ char const until_char = '=';
+
+ return settings_line_raw_read_until(0, out, len_req, len_read,
+ &until_char, cb_arg);
+}
+
+
+int settings_entry_copy(void *dst_ctx, off_t dst_off, void *src_ctx,
+ off_t src_off, size_t len)
+{
+ int rc;
+ char buf[16];
+ size_t chunk_size;
+
+ while (len) {
+ chunk_size = min(len, sizeof(buf));
+
+ rc = settings_io_cb.read_cb(src_ctx, src_off, buf, &chunk_size);
+ if (rc) {
+ break;
+ }
+
+ rc = settings_io_cb.write_cb(dst_ctx, dst_off, buf, chunk_size);
+ if (rc) {
+ break;
+ }
+
+ src_off += chunk_size;
+ dst_off += chunk_size;
+ len -= chunk_size;
+ }
+
+ return rc;
+}
+
+void settings_line_io_init(int (*read_cb)(void *ctx, off_t off, char *buf,
+ size_t *len),
+ int (*write_cb)(void *ctx, off_t off, char const *buf,
+ size_t len),
+ u8_t io_rwbs)
+{
+ settings_io_cb.read_cb = read_cb;
+ settings_io_cb.write_cb = write_cb;
+ settings_io_cb.rwbs = io_rwbs;
+}
diff --git a/subsys/settings/src/settings_priv.h b/subsys/settings/src/settings_priv.h
index 1a2ebe7..8964683 100644
--- a/subsys/settings/src/settings_priv.h
+++ b/subsys/settings/src/settings_priv.h
@@ -8,6 +8,9 @@
#ifndef __SETTINGS_PRIV_H_
#define __SETTINGS_PRIV_H_
+#include <sys/types.h>
+#include <errno.h>
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -24,18 +27,107 @@
int settings_line_make2(char *dst, int dlen, const char *name,
const char *value);
+
+
+void settings_line_io_init(int (*read_cb)(void *ctx, off_t off, char *buf,
+ size_t *len),
+ int (*write_cb)(void *ctx, off_t off,
+ char const *buf, size_t len),
+ u8_t io_rwbs);
+
+int settings_line_write(const char *name, const char *value, size_t val_len,
+ off_t w_loc, void *cb_arg);
+
+int settings_line_len_calc(const char *name, size_t val_len);
+
+#ifdef CONFIG_SETTINGS_ENCODE_LEN
+/* in storage line contex */
+struct line_entry_ctx {
+ void *stor_ctx;
+ off_t seek; /* offset of id-value pair within the file */
+ size_t len; /* len of line without len value */
+};
+
+int settings_next_line_ctx(struct line_entry_ctx *entry_ctx);
+#endif
+
+/**
+ * Read RAW settings line entry data from the storage.
+ *
+ * @param seek offset form the line beginning.
+ * @param[out] out buffer for name
+ * @param[in] len_req size of <p>out</p> buffer
+ * @param[out] len_read length of read name
+ * @param[in] cb_arg settings line storage context expected by the
+ * <p>read_cb</p> implementatio
+ *
+ * @retval 0 on success,
+ * -ERCODE on storage errors
+ */
+int settings_line_raw_read(off_t seek, char *out, size_t len_req,
+ size_t *len_read, void *cb_arg);
+
+/*
+ * @param val_off offset of the value-string.
+ * @param off from val_off (so within the value-string)
+ */
+int settings_line_val_read(off_t val_off, off_t off, char *out, size_t len_req,
+ size_t *len_read, void *cb_arg);
+
+/**
+ * Read the settings line entry name from the storage.
+ *
+ * @param[out] out buffer for name
+ * @param[in] len_req size of <p>out</p> buffer
+ * @param[out] len_read length of read name
+ * @param[in] cb_arg settings line storage context expected by the
+ * <p>read_cb</p> implementation
+ *
+ * @retval 0 on read proper name,
+ * 1 on when read improper name,
+ * -ERCODE on storage errors
+ */
+int settings_line_name_read(char *out, size_t len_req, size_t *len_read,
+ void *cb_arg);
+
+int settings_entry_copy(void *dst_ctx, off_t dst_off, void *src_ctx,
+ off_t src_off, size_t len);
+
+void settings_line_io_init(int (*read_cb)(void *ctx, off_t off, char *buf,
+ size_t *len),
+ int (*write_cb)(void *ctx, off_t off, char const *buf,
+ size_t len),
+ u8_t io_rwbs);
+
+int settings_set_value_priv(char *name, void *val_read_cb_ctx, off_t off,
+ u8_t is_runtime);
+
/*
* API for config storage.
*/
-typedef void (*load_cb)(char *name, char *val, void *cb_arg);
+typedef void (*load_cb)(char *name, void *val_read_cb_ctx, off_t off,
+ void *cb_arg);
+
struct settings_store_itf {
int (*csi_load)(struct settings_store *cs, load_cb cb, void *cb_arg);
int (*csi_save_start)(struct settings_store *cs);
int (*csi_save)(struct settings_store *cs, const char *name,
- const char *value);
+ const char *value, size_t val_len);
int (*csi_save_end)(struct settings_store *cs);
};
+struct read_value_cb_ctx {
+ void *read_cb_ctx;
+ off_t off;
+ u8_t runtime;
+};
+
+/* suports runtime settings_set_value operation */
+struct runtime_value_ctx {
+ void *p_value;
+ size_t size;
+};
+
void settings_src_register(struct settings_store *cs);
void settings_dst_register(struct settings_store *cs);
diff --git a/subsys/settings/src/settings_store.c b/subsys/settings/src/settings_store.c
index 6b478f5..35a35d3 100644
--- a/subsys/settings/src/settings_store.c
+++ b/subsys/settings/src/settings_store.c
@@ -20,6 +20,7 @@
struct settings_dup_check_arg {
const char *name;
const char *val;
+ size_t val_len;
int is_dup;
};
@@ -44,9 +45,10 @@
settings_save_dst = cs;
}
-static void settings_load_cb(char *name, char *val, void *cb_arg)
+static void settings_load_cb(char *name, void *val_read_cb_ctx, off_t off,
+ void *cb_arg)
{
- int rc = settings_set_value(name, val);
+ int rc = settings_set_value_priv(name, val_read_cb_ctx, off, 0);
__ASSERT(rc == 0, "set-value operation failure\n");
(void)rc;
}
@@ -68,22 +70,66 @@
return settings_commit(NULL);
}
-static void settings_dup_check_cb(char *name, char *val, void *cb_arg)
+/* val_off - offset of value-string within line entries */
+static int settings_cmp(char const *val, size_t val_len, void *val_read_cb_ctx,
+ off_t val_off)
+{
+ size_t len_read, exp_len;
+ size_t rem;
+ char buf[16];
+ int rc;
+ off_t off = 0;
+
+ for (rem = val_len; rem > 0; rem -= len_read) {
+ len_read = exp_len = min(sizeof(buf), rem);
+ rc = settings_line_val_read(val_off, off, buf, len_read,
+ &len_read, val_read_cb_ctx);
+ if (rc) {
+ break;
+ }
+
+ if (len_read != exp_len) {
+ rc = 1;
+ break;
+ }
+
+ rc = memcmp(val, buf, len_read);
+ if (rc) {
+ break;
+ }
+ val += len_read;
+ off += len_read;
+ }
+
+ return rc;
+}
+
+static void settings_dup_check_cb(char *name, void *val_read_cb_ctx, off_t off,
+ void *cb_arg)
{
struct settings_dup_check_arg *cdca = (struct settings_dup_check_arg *)
cb_arg;
+ size_t len_read;
+ char temp;
+ int rc;
+
if (strcmp(name, cdca->name)) {
return;
}
- if (!val) {
+
+ rc = settings_line_raw_read(off, &temp, 1, &len_read,
+ val_read_cb_ctx);
+ if (rc || len_read == 0 || temp == '\0') {
+ /* treat as an empty entry */
if (!cdca->val || cdca->val[0] == '\0') {
cdca->is_dup = 1;
} else {
cdca->is_dup = 0;
}
} else {
- if (cdca->val && !strcmp(val, cdca->val)) {
+ if (cdca->val && !settings_cmp(cdca->val, cdca->val_len,
+ val_read_cb_ctx, off)) {
cdca->is_dup = 1;
} else {
cdca->is_dup = 0;
@@ -94,7 +140,7 @@
/*
* Append a single value to persisted config. Don't store duplicate value.
*/
-int settings_save_one(const char *name, char *value)
+int settings_save_one(const char *name, void *value, size_t val_len)
{
struct settings_store *cs;
struct settings_dup_check_arg cdca;
@@ -108,13 +154,14 @@
* Check if we're writing the same value again.
*/
cdca.name = name;
- cdca.val = value;
+ cdca.val = (char *)value;
cdca.is_dup = 0;
+ cdca.val_len = val_len;
cs->cs_itf->csi_load(cs, settings_dup_check_cb, &cdca);
if (cdca.is_dup == 1) {
return 0;
}
- return cs->cs_itf->csi_save(cs, name, value);
+ return cs->cs_itf->csi_save(cs, name, (char *)value, val_len);
}
int settings_save(void)
@@ -136,8 +183,7 @@
SYS_SLIST_FOR_EACH_CONTAINER(&settings_handlers, ch, node) {
if (ch->h_export) {
- rc2 = ch->h_export(settings_save_one,
- SETTINGS_EXPORT_PERSIST);
+ rc2 = ch->h_export(settings_save_one);
if (!rc) {
rc = rc2;
}