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;
 			}