Prechádzať zdrojové kódy

add flashdb_test to googletest

Lyon 2 rokov pred
rodič
commit
198e52cc8c
40 zmenil súbory, kde vykonal 6224 pridanie a 1080 odobranie
  1. 1 1
      examples/flashdb/flashdb_utest.py
  2. 47 0
      package/flashdb/_flashdb.pyi
  3. 234 0
      package/flashdb/_flashdb_FlashDB.c
  4. 154 0
      package/flashdb/fdb.c
  5. 35 0
      package/flashdb/fdb_cfg.h
  6. 345 0
      package/flashdb/fdb_def.h
  7. 222 0
      package/flashdb/fdb_file.c
  8. 1854 0
      package/flashdb/fdb_kvdb.c
  9. 67 0
      package/flashdb/fdb_low_lvl.h
  10. 985 0
      package/flashdb/fdb_tsdb.c
  11. 320 0
      package/flashdb/fdb_utils.c
  12. 77 0
      package/flashdb/flashdb.h
  13. 29 0
      package/flashdb/flashdb.py
  14. 53 0
      package/flashdb/kvdb_basic_sample.c
  15. 63 0
      package/flashdb/kvdb_type_blob_sample.c
  16. 63 0
      package/flashdb/kvdb_type_string_sample.c
  17. 129 0
      package/flashdb/tsdb_sample.c
  18. 1 1
      port/linux/CMakeLists.txt
  19. 0 139
      port/linux/boot/demo06-pikamain/main.c
  20. 0 1
      port/linux/package/pikascript/main.py
  21. 147 140
      port/linux/package/pikascript/pikascript-lib/flashdb/_flashdb_FlashDB.c
  22. 33 20
      port/linux/package/pikascript/pikascript-lib/flashdb/fdb.c
  23. 2 1
      port/linux/package/pikascript/pikascript-lib/flashdb/fdb_cfg.h
  24. 188 140
      port/linux/package/pikascript/pikascript-lib/flashdb/fdb_def.h
  25. 44 39
      port/linux/package/pikascript/pikascript-lib/flashdb/fdb_file.c
  26. 318 216
      port/linux/package/pikascript/pikascript-lib/flashdb/fdb_kvdb.c
  27. 42 20
      port/linux/package/pikascript/pikascript-lib/flashdb/fdb_low_lvl.h
  28. 328 231
      port/linux/package/pikascript/pikascript-lib/flashdb/fdb_tsdb.c
  29. 77 53
      port/linux/package/pikascript/pikascript-lib/flashdb/fdb_utils.c
  30. 45 28
      port/linux/package/pikascript/pikascript-lib/flashdb/flashdb.h
  31. 10 7
      port/linux/package/pikascript/pikascript-lib/flashdb/kvdb_basic_sample.c
  32. 11 7
      port/linux/package/pikascript/pikascript-lib/flashdb/kvdb_type_blob_sample.c
  33. 6 5
      port/linux/package/pikascript/pikascript-lib/flashdb/kvdb_type_string_sample.c
  34. 43 26
      port/linux/package/pikascript/pikascript-lib/flashdb/tsdb_sample.c
  35. 2 1
      port/linux/package/pikascript/pikascript-lib/mqtt/mqttclient.c
  36. 5 0
      port/linux/package/pikascript/void.pyi
  37. 152 0
      port/linux/test/module-test.cpp
  38. 3 3
      port/linux/test/mqtt-test.cpp
  39. 88 0
      port/linux/test/python/flashdb/flashdb_utest.py
  40. 1 1
      src/PikaVersion.h

+ 1 - 1
port/linux/package/pikascript/flashdb_utest.py → examples/flashdb/flashdb_utest.py

@@ -85,4 +85,4 @@ def utest():
     runner = unittest.TextTestRunner()
     res = runner.run(suit)
     
-        
+utest() 

+ 47 - 0
package/flashdb/_flashdb.pyi

@@ -0,0 +1,47 @@
+class kvdb_t:
+    ...
+
+
+class FlashDB:
+    FDB_KVDB_CTRL_SET_SEC_SIZE: int
+    FDB_KVDB_CTRL_GET_SEC_SIZE: int
+    FDB_KVDB_CTRL_SET_LOCK: int
+    FDB_KVDB_CTRL_SET_UNLOCK: int
+    FDB_KVDB_CTRL_SET_FILE_MODE: int
+    FDB_KVDB_CTRL_SET_MAX_SIZE: int
+    FDB_KVDB_CTRL_SET_NOT_FORMAT: int
+
+    def __init__(self): ...
+
+    # def blob_make(blob:any, value_buf:any, buf_len:int)->any:...
+
+    # def blob_read(db:any, blob:any)->any:...
+
+    def kvdb_init(name: str, path: str, default_kv: dict,
+                  user_data: any) -> object: ...
+
+    def kvdb_control(kvdb: any, cmd: int, arg: any) -> int: ...
+
+    def kvdb_deinit(kvdb: any): ...
+
+    def kv_set_blob(kvdb: object, key: str, blob: any) -> int: ...
+
+    # def kv_set(kvdb:any, key:str, value:str)->int:...
+
+    def kv_get_blob(kvdb: object, key: str, size: int) -> list: ...
+
+    # def kv_get_obj(kvdb:any, key:str, kv:any)->any:...
+
+    # def kv_get(kvdb:any, key:str)->any:...
+
+    # def kv_del(kvdb:any, key:str)->int:...
+
+    # def kv_set_default(kvdb:any)->int:...
+
+    def kv_print(kvdb: object): ...
+
+    # def kv_to_blob(kv:any, blob:any)->any:...
+
+    # def kv_iterator_init(kvdb:any, itr:any)->any:...
+
+    # def kv_iterate(kvdb:any, itr:any)->any:...

+ 234 - 0
package/flashdb/_flashdb_FlashDB.c

@@ -0,0 +1,234 @@
+#include "_flashdb_FlashDB.h"
+#include <stdio.h>
+#include "flashdb.h"
+#include "pikaScript.h"
+#include "_flashdb_kvdb_t.h"
+
+//#include "fdb_def.h"
+#define PIKA_USING_FLASHDB1 1
+#if PIKA_USING_FLASHDB1
+//#include <pthread.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include "flashdb.h"
+#define FDB_LOG_TAG "[main]"
+
+bool g_kvdb_path_inited = false;
+
+/* TSDB object */
+// struct fdb_tsdb tsdb = { 0 };
+/* counts for simulated timestamp */
+// static int counts = 0;
+
+// extern void kvdb_basic_sample(fdb_kvdb_t kvdb);
+// extern void kvdb_type_string_sample(fdb_kvdb_t kvdb);
+// extern void kvdb_type_blob_sample(fdb_kvdb_t kvdb);
+// extern void tsdb_sample(fdb_tsdb_t tsdb);
+
+/*
+static void lock(fdb_db_t db)
+{
+    pthread_mutex_lock((pthread_mutex_t *)db->user_data);
+}
+
+static void unlock(fdb_db_t db)
+{
+    pthread_mutex_unlock((pthread_mutex_t *)db->user_data);
+}
+*/
+static fdb_time_t get_time(void) {
+    return time(NULL);
+}
+#endif
+
+/* KVDB object */
+static struct fdb_kvdb g_kvdb = {0};
+typedef struct fdb_kvdb FDB_KVDB;
+typedef struct fdb_default_kv_node FDB_DEFAULT_KV_NODE;
+int g_def_kv_table_idx;
+
+void _flashdb_FlashDB___init__(PikaObj* self) {}
+
+/*
+Arg* _flashdb_FlashDB_blob_make(PikaObj *self, Arg* blob, Arg* value_buf, int
+buf_len){ return NULL;
+}
+
+Arg* _flashdb_FlashDB_blob_read(PikaObj *self, Arg* db, Arg* blob){
+  return NULL;
+}
+
+int _flashdb_FlashDB_kv_del(PikaObj *self, Arg* kvdb, char* key){
+  return 0;
+}
+
+Arg* _flashdb_FlashDB_kv_get(PikaObj *self, Arg* kvdb, char* key){
+  return NULL;
+}
+*/
+PikaObj* _flashdb_FlashDB_kv_get_blob(PikaObj* self,
+                                      PikaObj* kvdb_in,
+                                      char* key,
+                                      int size) {
+    struct fdb_blob blob;
+    FDB_KVDB* kvdb = (FDB_KVDB*)obj_getPtr(kvdb_in, "kvdb");
+    blob.size = size;
+    uint8_t* buf = (uint8_t*)pikaMalloc(size + 1);
+    if (!buf) {
+        printf("alloc fail\n");
+        return NULL;
+    }
+    blob.buf = buf;
+    size_t len = fdb_kv_get_blob(kvdb, key, &blob);
+    if (len != size) {
+        printf("size error\n");
+        pikaFree(buf, size + 1);
+        return NULL;
+    }
+    PikaList* list = New_pikaList();
+    for (int i = 0; i < len; i++) {
+        pikaList_append(list, arg_newInt(buf[i]));
+    }
+    pikaFree(buf, size + 1);
+    return list;
+}
+
+Arg* _flashdb_FlashDB_kv_get_obj(PikaObj* self, Arg* kvdb, char* key, Arg* kv) {
+    return NULL;
+}
+
+Arg* _flashdb_FlashDB_kv_iterate(PikaObj* self, Arg* kvdb, Arg* itr) {
+    return NULL;
+}
+
+Arg* _flashdb_FlashDB_kv_iterator_init(PikaObj* self, Arg* kvdb, Arg* itr) {
+    return NULL;
+}
+
+void _flashdb_FlashDB_kv_print(PikaObj* self, PikaObj* kvdb_in) {
+    FDB_KVDB* kvdb = (FDB_KVDB*)obj_getPtr(kvdb_in, "kvdb");
+    fdb_kv_print(kvdb);
+}
+
+int _flashdb_FlashDB_kv_set(PikaObj* self, Arg* kvdb, char* key, char* value) {
+    return 0;
+}
+
+int _flashdb_FlashDB_kv_set_blob(PikaObj* self,
+                                 PikaObj* kvdb_in,
+                                 char* key,
+                                 Arg* blob_in) {
+    fdb_err_t res = FDB_NO_ERR;
+    FDB_KVDB* kvdb = (FDB_KVDB*)obj_getPtr(kvdb_in, "kvdb");
+
+    ArgType argt_blob_in = arg_getType(blob_in);
+    if (argt_blob_in != ARG_TYPE_BYTES) {
+        printf("blob must be bytes but got:%d", argt_blob_in);
+    }
+    size_t len = arg_getBytesSize(blob_in);
+    uint8_t* bytes = (uint8_t*)arg_getBytes(blob_in);
+
+    struct fdb_blob blob;
+    blob.size = len;
+    blob.buf = bytes;
+
+    res = fdb_kv_set_blob(kvdb, key, &blob);
+
+    return res;
+}
+
+int _flashdb_FlashDB_kv_set_default(PikaObj* self, Arg* kvdb) {
+    return 0;
+}
+
+Arg* _flashdb_FlashDB_kv_to_blob(PikaObj* self, Arg* kv, Arg* blob) {
+    return NULL;
+}
+
+int _flashdb_FlashDB_kvdb_control(PikaObj* self, Arg* kvdb, int cmd, Arg* arg) {
+    return 0;
+}
+
+void _flashdb_FlashDB_kvdb_deinit(PikaObj* self, Arg* kvdb) {}
+
+int32_t _flashdb_foreach(PikaObj* self,
+                         Arg* keyEach,
+                         Arg* valEach,
+                         void* context) {
+    char* key = arg_getStr(keyEach);
+    ArgType argt_val = arg_getType(valEach);
+    struct fdb_default_kv_node* def_kv_table =
+        (struct fdb_default_kv_node*)context;
+    if (argt_val == ARG_TYPE_STRING) {
+        char* val = arg_getStr(valEach);
+
+        def_kv_table[g_def_kv_table_idx].key = strdup(key);
+        def_kv_table[g_def_kv_table_idx].value = strdup(val);
+        def_kv_table[g_def_kv_table_idx].value_len = 0;
+        g_def_kv_table_idx++;
+
+    } else if (argt_val == ARG_TYPE_BYTES) {
+        size_t bytes_size = arg_getBytesSize(valEach);
+        uint8_t* bytes = arg_getBytes(valEach);
+        uint8_t* pbytes = (uint8_t*)malloc(bytes_size * sizeof(uint8_t));
+        memcpy(pbytes, bytes, bytes_size);
+        /*
+        for (size_t i=0; i < bytes_size; i++) {
+          printf("%02x", bytes[i]);
+        }
+        printf("\n");
+        */
+        def_kv_table[g_def_kv_table_idx].key = strdup(key);
+        def_kv_table[g_def_kv_table_idx].value = pbytes;
+        def_kv_table[g_def_kv_table_idx].value_len = bytes_size;
+        g_def_kv_table_idx++;
+    }
+    return 0;
+}
+
+PikaObj* _flashdb_FlashDB_kvdb_init(PikaObj* self,
+                                    char* name,
+                                    char* path,
+                                    PikaObj* default_kv_in,
+                                    Arg* user_data) {
+    printf("kvdb_init \n");
+
+    fdb_err_t result;
+    if (!g_kvdb_path_inited) {
+        bool file_mode = true;
+        uint32_t sec_size = 4096, db_size = sec_size * 4;
+
+        fdb_kvdb_control(&g_kvdb, FDB_KVDB_CTRL_SET_SEC_SIZE, &sec_size);
+        fdb_kvdb_control(&g_kvdb, FDB_KVDB_CTRL_SET_MAX_SIZE, &db_size);
+        /* enable file mode */
+        fdb_kvdb_control(&g_kvdb, FDB_KVDB_CTRL_SET_FILE_MODE, &file_mode);
+        /* create database directory */
+        mkdir(path, 0777);
+        g_kvdb_path_inited = true;
+    }
+    // int len =pikaDict_getSize(default_kv_in);
+
+    struct fdb_default_kv_node* def_kv_table =
+        (struct fdb_default_kv_node*)malloc(4 *
+                                            sizeof(struct fdb_default_kv_node));
+    g_def_kv_table_idx = 0;
+    pikaDict_forEach(default_kv_in, _flashdb_foreach, def_kv_table);
+
+    struct fdb_default_kv default_kv;
+
+    default_kv.kvs = def_kv_table;
+    default_kv.num = 4;
+
+    result =
+        fdb_kvdb_init(&g_kvdb, strdup(name), strdup(path), &default_kv, NULL);
+
+    if (result != FDB_NO_ERR) {
+        return NULL;
+    }
+
+    PikaObj* kvdb_obj = newNormalObj(New__flashdb_kvdb_t);
+    args_setStruct(kvdb_obj->list, "kvdb_struct", g_kvdb);
+    FDB_KVDB* pkvdb = args_getStruct(kvdb_obj->list, "kvdb_struct");
+    obj_setPtr(kvdb_obj, "kvdb", pkvdb);
+    return kvdb_obj;
+}

+ 154 - 0
package/flashdb/fdb.c

@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2020, Armink, <armink.ztl@gmail.com>
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/**
+ * @file
+ * @brief Initialize interface.
+ *
+ * Some initialize interface for this library.
+ */
+
+#include "flashdb.h"
+#include "fdb_low_lvl.h"
+#include <string.h>
+#include <inttypes.h>
+
+#define FDB_LOG_TAG ""
+
+#if !defined(FDB_USING_FAL_MODE) && !defined(FDB_USING_FILE_MODE)
+#error "Please defined the FDB_USING_FAL_MODE or FDB_USING_FILE_MODE macro"
+#endif
+
+fdb_err_t _fdb_init_ex(fdb_db_t db, const char *name, const char *path, fdb_db_type type, void *user_data)
+{
+    FDB_ASSERT(db);
+    FDB_ASSERT(name);
+    FDB_ASSERT(path);
+
+    if (db->init_ok) {
+        return FDB_NO_ERR;
+    }
+
+    db->name = name;
+    db->type = type;
+    db->user_data = user_data;
+
+    if (db->file_mode) {
+#ifdef FDB_USING_FILE_MODE
+        /* must set when using file mode */
+        FDB_ASSERT(db->sec_size != 0);
+        FDB_ASSERT(db->max_size != 0);
+#ifdef FDB_USING_FILE_POSIX_MODE
+        db->cur_file = -1;
+#else
+        db->cur_file = 0;
+#endif
+        db->storage.dir = path;
+        FDB_ASSERT(strlen(path) != 0)
+#endif
+    } else {
+#ifdef FDB_USING_FAL_MODE
+        size_t block_size;
+
+        /* FAL (Flash Abstraction Layer) initialization */
+        fal_init();
+        /* check the flash partition */
+        if ((db->storage.part = fal_partition_find(path)) == NULL) {
+            FDB_INFO("Error: Partition (%s) not found.\n", path);
+            return FDB_PART_NOT_FOUND;
+        }
+
+        block_size = fal_flash_device_find(db->storage.part->flash_name)->blk_size;
+        if (db->sec_size == 0) {
+            db->sec_size = block_size;
+        } else {
+	  // rbg/kcf
+            /* must be aligned with block size */
+            if (db->sec_size % block_size != 0) {
+	      //     	FDB_INFO("Error: db sector size (%" PRIu32 ") MUST align with block size (%" PRIu32 ").\n", db->sec_size, block_size);
+	      	FDB_INFO("Error: db sector size (%lu) MUST align with block size (%u).\n", db->sec_size, block_size);		
+	      
+                return FDB_INIT_FAILED;
+            }
+        }
+
+        db->max_size = db->storage.part->len;
+#endif /* FDB_USING_FAL_MODE */
+    }
+
+    /* the block size MUST to be the Nth power of 2 */
+    FDB_ASSERT((db->sec_size & (db->sec_size - 1)) == 0);
+    /* must align with sector size */
+    if (db->max_size % db->sec_size != 0) {
+        FDB_INFO("Error: db total size (%" PRIu32 ") MUST align with sector size (%" PRIu32 ").\n", db->max_size, db->sec_size);
+        return FDB_INIT_FAILED;
+    }
+    /* must has more than or equal 2 sectors */
+    if (db->max_size / db->sec_size < 2) {
+        FDB_INFO("Error: db MUST has more than or equal 2 sectors, current has %" PRIu32 " sector(s)\n", db->max_size / db->sec_size);
+        return FDB_INIT_FAILED;
+    }
+
+    return FDB_NO_ERR;
+}
+
+void _fdb_init_finish(fdb_db_t db, fdb_err_t result)
+{
+    static bool log_is_show = false;
+    if (result == FDB_NO_ERR) {
+        db->init_ok = true;
+        if (!log_is_show) {
+            FDB_INFO("FlashDB V%s is initialize success.\n", FDB_SW_VERSION);
+            FDB_INFO("You can get the latest version on https://github.com/armink/FlashDB .\n");
+            log_is_show = true;
+        }
+    } else if (!db->not_formatable) {
+        FDB_INFO("Error: %s (%s@%s) is initialize fail (%d).\n", db->type == FDB_DB_TYPE_KV ? "KVDB" : "TSDB",
+                db->name, _fdb_db_path(db), (int)result);
+    }
+}
+
+void _fdb_deinit(fdb_db_t db)
+{
+    FDB_ASSERT(db);
+
+    if (db->init_ok) {
+#ifdef FDB_USING_FILE_MODE
+#ifdef FDB_USING_FILE_POSIX_MODE
+        if (db->cur_file > 0) {
+#if !defined(_MSC_VER)
+#include <unistd.h>
+#endif
+            close(db->cur_file);
+        }
+#else
+        if (db->cur_file != 0) {
+            fclose(db->cur_file);
+        }
+#endif /* FDB_USING_FILE_POSIX_MODE */
+#endif /* FDB_USING_FILE_MODE */
+    }
+
+    db->init_ok = false;
+}
+
+const char *_fdb_db_path(fdb_db_t db)
+{
+    if (db->file_mode) {
+#ifdef FDB_USING_FILE_MODE
+        return db->storage.dir;
+#else
+        return NULL;
+#endif
+    }
+    else {
+#ifdef FDB_USING_FAL_MODE
+        return db->storage.part->name;
+#else
+        return NULL;
+#endif
+    }
+}

+ 35 - 0
package/flashdb/fdb_cfg.h

@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2020, Armink, <armink.ztl@gmail.com>
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/**
+ * @file
+ * @brief configuration file
+ */
+
+#ifndef _FDB_CFG_H_
+#define _FDB_CFG_H_
+
+/* using KVDB feature */
+#define FDB_USING_KVDB
+
+#ifdef FDB_USING_KVDB
+/* Auto update KV to latest default when current KVDB version number is changed. @see fdb_kvdb.ver_num */
+/* #define FDB_KV_AUTO_UPDATE */
+#endif
+
+/* using TSDB (Time series database) feature */
+#define FDB_USING_TSDB
+
+/* Using file storage mode by POSIX file API, like open/read/write/close */
+#define FDB_USING_FILE_POSIX_MODE
+
+/* log print macro. default EF_PRINT macro is printf() */
+/* #define FDB_PRINT(...)              my_printf(__VA_ARGS__) */
+
+/* print debug information */
+#define FDB_DEBUG_ENABLE
+
+#endif /* _FDB_CFG_H_ */

+ 345 - 0
package/flashdb/fdb_def.h

@@ -0,0 +1,345 @@
+/*
+ * Copyright (c) 2020-2023, Armink, <armink.ztl@gmail.com>
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/**
+ * @file
+ * @brief Public definition.
+ */
+
+#ifndef _FDB_DEF_H_
+#define _FDB_DEF_H_
+
+#include "PikaObj.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* software version number */
+#define FDB_SW_VERSION                 "2.0.0"
+#define FDB_SW_VERSION_NUM             0x20000
+
+/* the KV max name length must less then it */
+#ifndef FDB_KV_NAME_MAX
+#define FDB_KV_NAME_MAX                64
+#endif
+
+/* the KV cache table size, it will improve KV search speed when using cache */
+#ifndef FDB_KV_CACHE_TABLE_SIZE
+#define FDB_KV_CACHE_TABLE_SIZE        64
+#endif
+
+/* the sector cache table size, it will improve KV save speed when using cache */
+#ifndef FDB_SECTOR_CACHE_TABLE_SIZE
+#define FDB_SECTOR_CACHE_TABLE_SIZE    8
+#endif
+
+#if (FDB_KV_CACHE_TABLE_SIZE > 0) && (FDB_SECTOR_CACHE_TABLE_SIZE > 0)
+#define FDB_KV_USING_CACHE
+#endif
+
+#if defined(FDB_USING_FILE_LIBC_MODE) || defined(FDB_USING_FILE_POSIX_MODE)
+#define FDB_USING_FILE_MODE
+#endif
+
+#ifndef FDB_WRITE_GRAN
+#define FDB_WRITE_GRAN 1
+#endif
+
+/* log function. default FDB_PRINT macro is printf() */
+#ifndef FDB_PRINT
+#define FDB_PRINT(...)                 pika_platform_printf(__VA_ARGS__)
+#endif
+#define FDB_LOG_PREFIX1()              FDB_PRINT("[FlashDB]" FDB_LOG_TAG)
+#define FDB_LOG_PREFIX2()              FDB_PRINT(" ")
+#define FDB_LOG_PREFIX()               FDB_LOG_PREFIX1();FDB_LOG_PREFIX2()
+#ifdef FDB_DEBUG_ENABLE
+#define FDB_DEBUG(...)                 FDB_LOG_PREFIX();FDB_PRINT("(%s:%d) ", __FILE__, __LINE__);FDB_PRINT(__VA_ARGS__)
+#else
+#define FDB_DEBUG(...)
+#endif
+/* routine print function. Must be implement by user. */
+#define FDB_INFO(...)                  FDB_LOG_PREFIX();FDB_PRINT(__VA_ARGS__)
+/* assert for developer. */
+#define FDB_ASSERT(EXPR)                                                      \
+if (!(EXPR))                                                                  \
+{                                                                             \
+    FDB_INFO("(%s) has assert failed at %s.\n", #EXPR, __func__);             \
+    while (1);                                                                \
+}
+
+#define FDB_KVDB_CTRL_SET_SEC_SIZE     0x00             /**< set sector size control command, this change MUST before database initialization */
+#define FDB_KVDB_CTRL_GET_SEC_SIZE     0x01             /**< get sector size control command */
+#define FDB_KVDB_CTRL_SET_LOCK         0x02             /**< set lock function control command */
+#define FDB_KVDB_CTRL_SET_UNLOCK       0x03             /**< set unlock function control command */
+#define FDB_KVDB_CTRL_SET_FILE_MODE    0x09             /**< set file mode control command, this change MUST before database initialization */
+#define FDB_KVDB_CTRL_SET_MAX_SIZE     0x0A             /**< set database max size in file mode control command, this change MUST before database initialization */
+#define FDB_KVDB_CTRL_SET_NOT_FORMAT   0x0B             /**< set database NOT format mode control command, this change MUST before database initialization */
+
+#define FDB_TSDB_CTRL_SET_SEC_SIZE     0x00             /**< set sector size control command, this change MUST before database initialization */
+#define FDB_TSDB_CTRL_GET_SEC_SIZE     0x01             /**< get sector size control command */
+#define FDB_TSDB_CTRL_SET_LOCK         0x02             /**< set lock function control command */
+#define FDB_TSDB_CTRL_SET_UNLOCK       0x03             /**< set unlock function control command */
+#define FDB_TSDB_CTRL_SET_ROLLOVER     0x04             /**< set rollover control command, this change MUST after database initialization */
+#define FDB_TSDB_CTRL_GET_ROLLOVER     0x05             /**< get rollover control command */
+#define FDB_TSDB_CTRL_GET_LAST_TIME    0x06             /**< get last save time control command */
+#define FDB_TSDB_CTRL_SET_FILE_MODE    0x09             /**< set file mode control command, this change MUST before database initialization */
+#define FDB_TSDB_CTRL_SET_MAX_SIZE     0x0A             /**< set database max size in file mode control command, this change MUST before database initialization */
+#define FDB_TSDB_CTRL_SET_NOT_FORMAT   0x0B             /**< set database NOT formatable mode control command, this change MUST before database initialization */
+
+#define FDB_USING_TIMESTAMP_64BIT
+#ifdef FDB_USING_TIMESTAMP_64BIT
+    typedef int64_t fdb_time_t;
+#else
+    typedef int32_t fdb_time_t;
+#endif /* FDB_USING_TIMESTAMP_64BIT */
+
+typedef fdb_time_t (*fdb_get_time)(void);
+
+struct fdb_default_kv_node {
+    char *key;
+    void *value;
+    size_t value_len;
+};
+
+struct fdb_default_kv {
+    struct fdb_default_kv_node *kvs;
+    size_t num;
+};
+
+/* error code */
+typedef enum {
+    FDB_NO_ERR,
+    FDB_ERASE_ERR,
+    FDB_READ_ERR,
+    FDB_WRITE_ERR,
+    FDB_PART_NOT_FOUND,
+    FDB_KV_NAME_ERR,
+    FDB_KV_NAME_EXIST,
+    FDB_SAVED_FULL,
+    FDB_INIT_FAILED,
+} fdb_err_t;
+
+enum fdb_kv_status {
+    FDB_KV_UNUSED,
+    FDB_KV_PRE_WRITE,
+    FDB_KV_WRITE,
+    FDB_KV_PRE_DELETE,
+    FDB_KV_DELETED,
+    FDB_KV_ERR_HDR,
+#define FDB_KV_STATUS_NUM                        6
+};
+typedef enum fdb_kv_status fdb_kv_status_t;
+
+enum fdb_tsl_status {
+    FDB_TSL_UNUSED,
+    FDB_TSL_PRE_WRITE,
+    FDB_TSL_WRITE,
+    FDB_TSL_USER_STATUS1,
+    FDB_TSL_DELETED,
+    FDB_TSL_USER_STATUS2,
+#define FDB_TSL_STATUS_NUM                       6
+};
+typedef enum fdb_tsl_status fdb_tsl_status_t;
+
+/* key-value node object */
+struct fdb_kv {
+    fdb_kv_status_t status;                      /**< node status, @see fdb_kv_status_t */
+    bool crc_is_ok;                              /**< node CRC32 check is OK */
+    uint8_t name_len;                            /**< name length */
+    uint32_t magic;                              /**< magic word(`K`, `V`, `4`, `0`) */
+    // uint32_t len;                                /**< node total length (header + name + value), must align by FDB_WRITE_GRAN */
+    unsigned long len;                                /**< node total length (header + name + value), must align by FDB_WRITE_GRAN */
+    uint32_t value_len;                          /**< value length */
+    char name[FDB_KV_NAME_MAX];                  /**< name */
+    struct {
+        uint32_t start;                          /**< node start address */
+        uint32_t value;                          /**< value start address */
+    } addr;
+};
+typedef struct fdb_kv *fdb_kv_t;
+
+struct fdb_kv_iterator {
+    struct fdb_kv curr_kv;                       /**< Current KV we get from the iterator */
+    uint32_t iterated_cnt;                       /**< How many KVs have we iterated already */
+    size_t iterated_obj_bytes;                   /**< Total storage size of KVs we have iterated. */
+    size_t iterated_value_bytes;                 /**< Total value size of KVs we have iterated. */
+    uint32_t sector_addr;                        /**< Current sector address we're iterating. DO NOT touch it. */
+    uint32_t traversed_len;                      /**< Traversed sector total length. */
+};
+typedef struct fdb_kv_iterator *fdb_kv_iterator_t;
+
+/* time series log node object */
+struct fdb_tsl {
+    fdb_tsl_status_t status;                     /**< node status, @see fdb_log_status_t */
+    fdb_time_t time;                             /**< node timestamp */
+    uint32_t log_len;                            /**< log length, must align by FDB_WRITE_GRAN */
+    struct {
+        uint32_t index;                          /**< node index address */
+        uint32_t log;                            /**< log data address */
+    } addr;
+};
+typedef struct fdb_tsl *fdb_tsl_t;
+typedef bool (*fdb_tsl_cb)(fdb_tsl_t tsl, void *arg);
+
+typedef enum {
+    FDB_DB_TYPE_KV,
+    FDB_DB_TYPE_TS,
+} fdb_db_type;
+
+/* the flash sector store status */
+enum fdb_sector_store_status {
+    FDB_SECTOR_STORE_UNUSED,
+    FDB_SECTOR_STORE_EMPTY,
+    FDB_SECTOR_STORE_USING,
+    FDB_SECTOR_STORE_FULL,
+#define FDB_SECTOR_STORE_STATUS_NUM              4
+};
+typedef enum fdb_sector_store_status fdb_sector_store_status_t;
+
+/* the flash sector dirty status */
+enum fdb_sector_dirty_status {
+    FDB_SECTOR_DIRTY_UNUSED,
+    FDB_SECTOR_DIRTY_FALSE,
+    FDB_SECTOR_DIRTY_TRUE,
+    FDB_SECTOR_DIRTY_GC,
+#define FDB_SECTOR_DIRTY_STATUS_NUM              4
+};
+typedef enum fdb_sector_dirty_status fdb_sector_dirty_status_t;
+
+/* KVDB section information */
+struct kvdb_sec_info {
+    bool check_ok;                               /**< sector header check is OK */
+    struct {
+        fdb_sector_store_status_t store;         /**< sector store status @see fdb_sector_store_status_t */
+        fdb_sector_dirty_status_t dirty;         /**< sector dirty status @see sector_dirty_status_t */
+    } status;
+    uint32_t addr;                               /**< sector start address */
+    uint32_t magic;                              /**< magic word(`E`, `F`, `4`, `0`) */
+    uint32_t combined;                           /**< the combined next sector number, 0xFFFFFFFF: not combined */
+    size_t remain;                               /**< remain size */
+    uint32_t empty_kv;                           /**< the next empty KV node start address */
+};
+typedef struct kvdb_sec_info *kv_sec_info_t;
+
+/* TSDB section information */
+struct tsdb_sec_info {
+    bool check_ok;                               /**< sector header check is OK */
+    fdb_sector_store_status_t status;            /**< sector store status @see fdb_sector_store_status_t */
+    uint32_t addr;                               /**< sector start address */
+    uint32_t magic;                              /**< magic word(`T`, `S`, `L`, `0`) */
+    fdb_time_t start_time;                       /**< the first start node's timestamp, 0xFFFFFFFF: unused */
+    fdb_time_t end_time;                         /**< the last end node's timestamp, 0xFFFFFFFF: unused */
+    uint32_t end_idx;                            /**< the last end node's index, 0xFFFFFFFF: unused */
+    fdb_tsl_status_t end_info_stat[2];           /**< the last end node's info status */
+    size_t remain;                               /**< remain size */
+    uint32_t empty_idx;                          /**< the next empty node index address */
+    uint32_t empty_data;                         /**< the next empty node's data end address */
+};
+typedef struct tsdb_sec_info *tsdb_sec_info_t;
+
+struct kv_cache_node {
+    uint16_t name_crc;                           /**< KV name's CRC32 low 16bit value */
+    uint16_t active;                             /**< KV node access active degree */
+    uint32_t addr;                               /**< KV node address */
+};
+typedef struct kv_cache_node *kv_cache_node_t;
+
+struct sector_cache_node {
+    uint32_t addr;                               /**< sector start address */
+    uint32_t empty_addr;                         /**< sector empty address */
+};
+typedef struct sector_cache_node *sector_cache_node_t;
+
+/* database structure */
+typedef struct fdb_db *fdb_db_t;
+struct fdb_db {
+    const char *name;                            /**< database name */
+    fdb_db_type type;                            /**< database type */
+    union {
+#ifdef FDB_USING_FAL_MODE
+        const struct fal_partition *part;        /**< flash partition for saving database */
+#endif
+#ifdef FDB_USING_FILE_MODE
+        const char *dir;                         /**< directory path for saving database */
+#endif
+    } storage;
+    uint32_t sec_size;                           /**< flash section size. It's a multiple of block size */
+    uint32_t max_size;                           /**< database max size. It's a multiple of section size */
+    uint32_t oldest_addr;                        /**< the oldest sector start address */
+    bool init_ok;                                /**< initialized successfully */
+    bool file_mode;                              /**< is file mode, default is false */
+    bool not_formatable;                         /**< is can NOT be formated mode, default is false */
+#ifdef FDB_USING_FILE_MODE
+#if defined(FDB_USING_FILE_POSIX_MODE)
+    int cur_file;                                /**< current file object */
+#elif defined(FDB_USING_FILE_LIBC_MODE)
+    FILE *cur_file;                              /**< current file object */
+#endif
+    uint32_t cur_sec;                            /**< current operate sector address  */
+#endif
+    void (*lock)(fdb_db_t db);                   /**< lock the database operate */
+    void (*unlock)(fdb_db_t db);                 /**< unlock the database operate */
+
+    void *user_data;
+};
+
+/* KVDB structure */
+struct fdb_kvdb {
+    struct fdb_db parent;                        /**< inherit from fdb_db */
+    struct fdb_default_kv default_kvs;           /**< default KV */
+    bool gc_request;                             /**< request a GC check */
+    bool in_recovery_check;                      /**< is in recovery check status when first reboot */
+    struct fdb_kv cur_kv;
+    struct kvdb_sec_info cur_sector;
+    bool last_is_complete_del;
+
+#ifdef FDB_KV_USING_CACHE
+    /* KV cache table */
+    struct kv_cache_node kv_cache_table[FDB_KV_CACHE_TABLE_SIZE];
+    /* sector cache table, it caching the sector info which status is current using */
+    struct sector_cache_node sector_cache_table[FDB_SECTOR_CACHE_TABLE_SIZE];
+#endif /* FDB_KV_USING_CACHE */
+
+#ifdef FDB_KV_AUTO_UPDATE
+    uint32_t ver_num;                            /**< setting version number for update */
+#endif
+
+    void *user_data;
+};
+typedef struct fdb_kvdb *fdb_kvdb_t;
+
+/* TSDB structure */
+struct fdb_tsdb {
+    struct fdb_db parent;                        /**< inherit from fdb_db */
+    struct tsdb_sec_info cur_sec;                /**< current using sector */
+    fdb_time_t last_time;                        /**< last TSL timestamp */
+    fdb_get_time get_time;                       /**< the current timestamp get function */
+    size_t max_len;                              /**< the maximum length of each log */
+    bool rollover;                               /**< the oldest data will rollover by newest data, default is true */
+
+    void *user_data;
+};
+typedef struct fdb_tsdb *fdb_tsdb_t;
+
+/* blob structure */
+struct fdb_blob {
+    void *buf;                                   /**< blob data buffer */
+    size_t size;                                 /**< blob data buffer size */
+    struct {
+        uint32_t meta_addr;                      /**< saved KV or TSL index address */
+        uint32_t addr;                           /**< blob data saved address */
+        size_t len;                              /**< blob data saved length */
+    } saved;
+};
+typedef struct fdb_blob *fdb_blob_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FDB_DEF_H_ */
+

+ 222 - 0
package/flashdb/fdb_file.c

@@ -0,0 +1,222 @@
+/*
+ * Copyright (c) 2020, Armink, <armink.ztl@gmail.com>
+ * Copyright (c) 2020, enkiller, <462747508@qq.com>
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "flashdb.h"
+#include "fdb_low_lvl.h"
+
+#define FDB_LOG_TAG "[file]"
+
+#ifdef FDB_USING_FILE_MODE
+
+#define DB_PATH_MAX            256
+
+static void get_db_file_path(fdb_db_t db, uint32_t addr, char *path, size_t size)
+{
+#define DB_NAME_MAX            8
+
+    /* from db_name.fdb.0 to db_name.fdb.n */
+    char file_name[DB_NAME_MAX + 4 + 10];
+    uint32_t sec_addr = FDB_ALIGN_DOWN(addr, db->sec_size);
+    int index = sec_addr / db->sec_size;
+
+    snprintf(file_name, sizeof(file_name), "%.*s.fdb.%d", DB_NAME_MAX, db->name, index);
+    if (strlen(db->storage.dir) + 1 + strlen(file_name) >= size) {
+        /* path is too long */
+        FDB_ASSERT(0)
+    }
+    snprintf(path, size, "%s/%s", db->storage.dir, file_name);
+}
+
+#if defined(FDB_USING_FILE_POSIX_MODE)
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#if !defined(_MSC_VER)
+#include <unistd.h>
+#endif
+
+static int open_db_file(fdb_db_t db, uint32_t addr, bool clean)
+{
+    uint32_t sec_addr = FDB_ALIGN_DOWN(addr, db->sec_size);
+    int fd = db->cur_file;
+    char path[DB_PATH_MAX];
+
+    if (sec_addr != db->cur_sec || fd <= 0 || clean) {
+        get_db_file_path(db, addr, path, DB_PATH_MAX);
+
+        if (fd > 0) {
+            close(fd);
+            fd = -1;
+        }
+        if (clean) {
+            /* clean the old file */
+            fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0777);
+            if (fd <= 0) {
+                FDB_INFO("Error: open (%s) file failed.\n", path);
+            }
+            else {
+                close(fd);
+                fd = -1;
+            }
+        }
+        /* open the database file */
+        fd = open(path, O_RDWR, 0777);
+        db->cur_sec = sec_addr;
+    }
+    db->cur_file = fd;
+
+    return db->cur_file;
+}
+
+fdb_err_t _fdb_file_read(fdb_db_t db, uint32_t addr, void *buf, size_t size)
+{
+    fdb_err_t result = FDB_NO_ERR;
+    int fd = open_db_file(db, addr, false);
+    if (fd > 0) {
+        /* get the offset address is relative to the start of the current file */
+        addr = addr % db->sec_size;
+        if ((lseek(fd, addr, SEEK_SET) != addr) || (read(fd, buf, size) != size))
+            result = FDB_READ_ERR;
+    } else {
+        result = FDB_READ_ERR;
+    }
+    return result;
+}
+
+fdb_err_t _fdb_file_write(fdb_db_t db, uint32_t addr, const void *buf, size_t size, bool sync)
+{
+    fdb_err_t result = FDB_NO_ERR;
+    int fd = open_db_file(db, addr, false);
+    if (fd > 0) {
+        /* get the offset address is relative to the start of the current file */
+        addr = addr % db->sec_size;
+        if ((lseek(fd, addr, SEEK_SET) != addr) || (write(fd, buf, size) != size))
+            result = FDB_WRITE_ERR;
+        if(sync) {
+            fsync(fd);
+        }
+    } else {
+        result = FDB_WRITE_ERR;
+    }
+    return result;
+}
+
+fdb_err_t _fdb_file_erase(fdb_db_t db, uint32_t addr, size_t size)
+{
+    fdb_err_t result = FDB_NO_ERR;
+    int fd = open_db_file(db, addr, true);
+    if (fd > 0) {
+#define BUF_SIZE 32
+        uint8_t buf[BUF_SIZE];
+        size_t i;
+        lseek(fd, 0, SEEK_SET);
+        for (i = 0; i * BUF_SIZE < size; i++)
+        {
+            memset(buf, 0xFF, BUF_SIZE);
+            write(fd, buf, BUF_SIZE);
+        }
+        memset(buf, 0xFF, BUF_SIZE);
+        write(fd, buf, size - i * BUF_SIZE);
+        fsync(fd);
+    } else {
+        result = FDB_ERASE_ERR;
+    }
+    return result;
+}
+#elif defined(FDB_USING_FILE_LIBC_MODE)
+static FILE *open_db_file(fdb_db_t db, uint32_t addr, bool clean)
+{
+    uint32_t sec_addr = FDB_ALIGN_DOWN(addr, db->sec_size);
+
+    if (sec_addr != db->cur_sec || db->cur_file == NULL || clean) {
+        char path[DB_PATH_MAX];
+
+        get_db_file_path(db, addr, path, DB_PATH_MAX);
+
+        if (db->cur_file) {
+            fclose(db->cur_file);
+        }
+
+        if (clean) {
+            /* clean the old file */
+            db->cur_file = fopen(path, "wb+");
+            if (db->cur_file == NULL) {
+                FDB_INFO("Error: open (%s) file failed.\n", path);
+            } else {
+                fclose(db->cur_file);
+            }
+        }
+
+        /* open the database file */
+        db->cur_file = fopen(path, "rb+");
+        db->cur_sec = sec_addr;
+    }
+
+    return db->cur_file;
+}
+
+fdb_err_t _fdb_file_read(fdb_db_t db, uint32_t addr, void *buf, size_t size)
+{
+    fdb_err_t result = FDB_NO_ERR;
+    FILE *fp = open_db_file(db, addr, false);
+    if (fp) {
+        addr = addr % db->sec_size;
+        if ((fseek(fp, addr, SEEK_SET) != 0) || (fread(buf, size, 1, fp) != size))
+            result = FDB_READ_ERR;
+    } else {
+        result = FDB_READ_ERR;
+    }
+    return result;
+}
+
+fdb_err_t _fdb_file_write(fdb_db_t db, uint32_t addr, const void *buf, size_t size, bool sync)
+{
+    fdb_err_t result = FDB_NO_ERR;
+    FILE *fp = open_db_file(db, addr, false);
+    if (fp) {
+        addr = addr % db->sec_size;
+        if ((fseek(fp, addr, SEEK_SET) != 0) || (fwrite(buf, size, 1, fp) != size))
+            result = FDB_READ_ERR;
+        if(sync) {
+            fflush(fp);
+        }
+    } else {
+        result = FDB_READ_ERR;
+    }
+
+    return result;
+}
+
+fdb_err_t _fdb_file_erase(fdb_db_t db, uint32_t addr, size_t size)
+{
+    fdb_err_t result = FDB_NO_ERR;
+
+    FILE *fp = open_db_file(db, addr, true);
+    if (fp != NULL) {
+#define BUF_SIZE 32
+        uint8_t buf[BUF_SIZE];
+        size_t i;
+        fseek(fp, 0, SEEK_SET);
+        for (i = 0; i * BUF_SIZE < size; i++)
+        {
+            memset(buf, 0xFF, BUF_SIZE);
+            fwrite(buf, BUF_SIZE, 1, fp);
+        }
+        memset(buf, 0xFF, BUF_SIZE);
+        fwrite(buf, size - i * BUF_SIZE, 1, fp);
+        fflush(fp);
+    } else {
+        result = FDB_ERASE_ERR;
+    }
+    return result;
+}
+#endif /* defined(FDB_USING_FILE_LIBC_MODE) */
+
+#endif /* FDB_USING_FILE_MODE */
+

+ 1854 - 0
package/flashdb/fdb_kvdb.c

@@ -0,0 +1,1854 @@
+/*
+ * Copyright (c) 2020, Armink, <armink.ztl@gmail.com>
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/**
+ * @file
+ * @brief KVDB feature.
+ *
+ * Key-Value Database feature implement source file.
+ */
+
+#include <inttypes.h>
+#include <string.h>
+#include "flashdb.h"
+#include "fdb_low_lvl.h"
+
+#define FDB_LOG_TAG "[kv]"
+/* rewrite log prefix */
+#undef  FDB_LOG_PREFIX2
+#define FDB_LOG_PREFIX2()                         FDB_PRINT("[%s][%s] ", db_name(db), _fdb_db_path((fdb_db_t)db))
+
+#if defined(FDB_USING_KVDB)
+
+#ifndef FDB_WRITE_GRAN
+#error "Please configure flash write granularity (in fdb_cfg.h)"
+#endif
+
+#if FDB_WRITE_GRAN != 1 && FDB_WRITE_GRAN != 8 && FDB_WRITE_GRAN != 32 && FDB_WRITE_GRAN != 64
+#error "the write gran can be only setting as 1, 8, 32 and 64"
+#endif
+
+/* magic word(`F`, `D`, `B`, `1`) */
+#define SECTOR_MAGIC_WORD                        0x30424446
+/* magic word(`K`, `V`, `0`, `0`) */
+#define KV_MAGIC_WORD                            0x3030564B
+
+/* the sector remain threshold before full status */
+#ifndef FDB_SEC_REMAIN_THRESHOLD
+#define FDB_SEC_REMAIN_THRESHOLD                  (KV_HDR_DATA_SIZE + FDB_KV_NAME_MAX)
+#endif
+
+/* the total remain empty sector threshold before GC */
+#ifndef FDB_GC_EMPTY_SEC_THRESHOLD
+#define FDB_GC_EMPTY_SEC_THRESHOLD                1
+#endif
+
+/* the string KV value buffer size for legacy fdb_get_kv(db, ) function */
+#ifndef FDB_STR_KV_VALUE_MAX_SIZE
+#define FDB_STR_KV_VALUE_MAX_SIZE                128
+#endif
+
+#if FDB_KV_CACHE_TABLE_SIZE > 0xFFFF
+#error "The KV cache table size must less than 0xFFFF"
+#endif
+
+/* the sector is not combined value */
+#if (FDB_BYTE_ERASED  == 0xFF)
+#define SECTOR_NOT_COMBINED                      0xFFFFFFFF
+#else
+#define SECTOR_NOT_COMBINED                      0x00000000
+#endif
+/* the next address is get failed */
+#define FAILED_ADDR                              0xFFFFFFFF
+
+#define KV_STATUS_TABLE_SIZE                     FDB_STATUS_TABLE_SIZE(FDB_KV_STATUS_NUM)
+
+#define SECTOR_NUM                               (db_max_size(db) / db_sec_size(db))
+
+#define SECTOR_HDR_DATA_SIZE                     (FDB_WG_ALIGN(sizeof(struct sector_hdr_data)))
+#define SECTOR_STORE_OFFSET                      ((unsigned long)(&((struct sector_hdr_data *)0)->status_table.store))
+#define SECTOR_DIRTY_OFFSET                      ((unsigned long)(&((struct sector_hdr_data *)0)->status_table.dirty))
+#define SECTOR_MAGIC_OFFSET                      ((unsigned long)(&((struct sector_hdr_data *)0)->magic))
+#define KV_HDR_DATA_SIZE                         (FDB_WG_ALIGN(sizeof(struct kv_hdr_data)))
+#define KV_MAGIC_OFFSET                          ((unsigned long)(&((struct kv_hdr_data *)0)->magic))
+#define KV_LEN_OFFSET                            ((unsigned long)(&((struct kv_hdr_data *)0)->len))
+#define KV_NAME_LEN_OFFSET                       ((unsigned long)(&((struct kv_hdr_data *)0)->name_len))
+
+#define db_name(db)                              (((fdb_db_t)db)->name)
+#define db_init_ok(db)                           (((fdb_db_t)db)->init_ok)
+#define db_sec_size(db)                          (((fdb_db_t)db)->sec_size)
+#define db_max_size(db)                          (((fdb_db_t)db)->max_size)
+#define db_oldest_addr(db)                       (((fdb_db_t)db)->oldest_addr)
+
+#define db_lock(db)                                                            \
+    do {                                                                       \
+        if (((fdb_db_t)db)->lock) ((fdb_db_t)db)->lock((fdb_db_t)db);          \
+    } while(0);
+
+#define db_unlock(db)                                                          \
+    do {                                                                       \
+        if (((fdb_db_t)db)->unlock) ((fdb_db_t)db)->unlock((fdb_db_t)db);      \
+    } while(0);
+
+#define VER_NUM_KV_NAME                         "__ver_num__"
+
+struct sector_hdr_data {
+    struct {
+        uint8_t store[FDB_STORE_STATUS_TABLE_SIZE];  /**< sector store status @see fdb_sector_store_status_t */
+        uint8_t dirty[FDB_DIRTY_STATUS_TABLE_SIZE];  /**< sector dirty status @see fdb_sector_dirty_status_t */
+    } status_table;
+    uint32_t magic;                              /**< magic word(`E`, `F`, `4`, `0`) */
+    uint32_t combined;                           /**< the combined next sector number, 0xFFFFFFFF: not combined */
+    uint32_t reserved;
+#if (FDB_WRITE_GRAN == 64)
+    uint8_t padding[4];                          /**< align padding for 64bit write granularity */
+#endif
+};
+typedef struct sector_hdr_data *sector_hdr_data_t;
+
+struct kv_hdr_data {
+    uint8_t status_table[KV_STATUS_TABLE_SIZE];  /**< KV node status, @see fdb_kv_status_t */
+    uint32_t magic;                              /**< magic word(`K`, `V`, `4`, `0`) */
+    uint32_t len;                                /**< KV node total length (header + name + value), must align by FDB_WRITE_GRAN */
+    uint32_t crc32;                              /**< KV node crc32(name_len + data_len + name + value) */
+    uint8_t name_len;                            /**< name length */
+    uint32_t value_len;                          /**< value length */
+#if (FDB_WRITE_GRAN == 64)
+    uint8_t padding[4];                          /**< align padding for 64bit write granularity */
+#endif
+};
+typedef struct kv_hdr_data *kv_hdr_data_t;
+
+struct alloc_kv_cb_args {
+    fdb_kvdb_t db;
+    size_t kv_size;
+    uint32_t *empty_kv;
+};
+
+struct gc_cb_args {
+    fdb_kvdb_t db;
+    size_t cur_free_size;
+    size_t setting_free_size;
+    uint32_t traversed_len;
+};
+
+static void gc_collect(fdb_kvdb_t db);
+static void gc_collect_by_free_size(fdb_kvdb_t db, size_t free_size);
+
+#ifdef FDB_KV_USING_CACHE
+/*
+ * It's only caching the current using status sector's empty_addr
+ */
+static void update_sector_cache(fdb_kvdb_t db, uint32_t sec_addr, uint32_t empty_addr)
+{
+    size_t i, empty_index = FDB_SECTOR_CACHE_TABLE_SIZE;
+
+    for (i = 0; i < FDB_SECTOR_CACHE_TABLE_SIZE; i++) {
+        if ((empty_addr > sec_addr) && (empty_addr < sec_addr + db_sec_size(db))) {
+            /* update the sector empty_addr in cache */
+            if (db->sector_cache_table[i].addr == sec_addr) {
+                db->sector_cache_table[i].addr = sec_addr;
+                db->sector_cache_table[i].empty_addr = empty_addr;
+                return;
+            } else if ((db->sector_cache_table[i].addr == FDB_DATA_UNUSED) && (empty_index == FDB_SECTOR_CACHE_TABLE_SIZE)) {
+                empty_index = i;
+            }
+        } else if (db->sector_cache_table[i].addr == sec_addr) {
+            /* delete the sector which status is not current using */
+            db->sector_cache_table[i].addr = FDB_DATA_UNUSED;
+            return;
+        }
+    }
+    /* add the sector empty_addr to cache */
+    if (empty_index < FDB_SECTOR_CACHE_TABLE_SIZE) {
+        db->sector_cache_table[empty_index].addr = sec_addr;
+        db->sector_cache_table[empty_index].empty_addr = empty_addr;
+    }
+}
+
+/*
+ * Get sector info from cache. It's return true when cache is hit.
+ */
+static bool get_sector_from_cache(fdb_kvdb_t db, uint32_t sec_addr, uint32_t *empty_addr)
+{
+    size_t i;
+
+    for (i = 0; i < FDB_SECTOR_CACHE_TABLE_SIZE; i++) {
+        if (db->sector_cache_table[i].addr == sec_addr) {
+            if (empty_addr) {
+                *empty_addr = db->sector_cache_table[i].empty_addr;
+            }
+            return true;
+        }
+    }
+
+    return false;
+}
+
+static void update_kv_cache(fdb_kvdb_t db, const char *name, size_t name_len, uint32_t addr)
+{
+    size_t i, empty_index = FDB_KV_CACHE_TABLE_SIZE, min_activity_index = FDB_KV_CACHE_TABLE_SIZE;
+    uint16_t name_crc = (uint16_t) (fdb_calc_crc32(0, name, name_len) >> 16), min_activity = 0xFFFF;
+
+    for (i = 0; i < FDB_KV_CACHE_TABLE_SIZE; i++) {
+        if (addr != FDB_DATA_UNUSED) {
+            /* update the KV address in cache */
+            if (db->kv_cache_table[i].name_crc == name_crc) {
+                db->kv_cache_table[i].addr = addr;
+                return;
+            } else if ((db->kv_cache_table[i].addr == FDB_DATA_UNUSED) && (empty_index == FDB_KV_CACHE_TABLE_SIZE)) {
+                empty_index = i;
+            } else if (db->kv_cache_table[i].addr != FDB_DATA_UNUSED) {
+                if (db->kv_cache_table[i].active > 0) {
+                    db->kv_cache_table[i].active--;
+                }
+                if (db->kv_cache_table[i].active < min_activity) {
+                    min_activity_index = i;
+                    min_activity = db->kv_cache_table[i].active;
+                }
+            }
+        } else if (db->kv_cache_table[i].name_crc == name_crc) {
+            /* delete the KV */
+            db->kv_cache_table[i].addr = FDB_DATA_UNUSED;
+            db->kv_cache_table[i].active = 0;
+            return;
+        }
+    }
+    /* add the KV to cache, using LRU (Least Recently Used) like algorithm */
+    if (empty_index < FDB_KV_CACHE_TABLE_SIZE) {
+        db->kv_cache_table[empty_index].addr = addr;
+        db->kv_cache_table[empty_index].name_crc = name_crc;
+        db->kv_cache_table[empty_index].active = FDB_KV_CACHE_TABLE_SIZE;
+    } else if (min_activity_index < FDB_KV_CACHE_TABLE_SIZE) {
+        db->kv_cache_table[min_activity_index].addr = addr;
+        db->kv_cache_table[min_activity_index].name_crc = name_crc;
+        db->kv_cache_table[min_activity_index].active = FDB_KV_CACHE_TABLE_SIZE;
+    }
+}
+
+/*
+ * Get KV info from cache. It's return true when cache is hit.
+ */
+static bool get_kv_from_cache(fdb_kvdb_t db, const char *name, size_t name_len, uint32_t *addr)
+{
+    size_t i;
+    uint16_t name_crc = (uint16_t) (fdb_calc_crc32(0, name, name_len) >> 16);
+
+    for (i = 0; i < FDB_KV_CACHE_TABLE_SIZE; i++) {
+        if ((db->kv_cache_table[i].addr != FDB_DATA_UNUSED) && (db->kv_cache_table[i].name_crc == name_crc)) {
+            char saved_name[FDB_KV_NAME_MAX] = { 0 };
+            /* read the KV name in flash */
+            _fdb_flash_read((fdb_db_t)db, db->kv_cache_table[i].addr + KV_HDR_DATA_SIZE, (uint32_t *) saved_name, FDB_KV_NAME_MAX);
+            if (!strncmp(name, saved_name, name_len)) {
+                *addr = db->kv_cache_table[i].addr;
+                if (db->kv_cache_table[i].active >= 0xFFFF - FDB_KV_CACHE_TABLE_SIZE) {
+                    db->kv_cache_table[i].active = 0xFFFF;
+                } else {
+                    db->kv_cache_table[i].active += FDB_KV_CACHE_TABLE_SIZE;
+                }
+                return true;
+            }
+        }
+    }
+
+    return false;
+}
+#endif /* FDB_KV_USING_CACHE */
+
+/*
+ * find the next KV address by magic word on the flash
+ */
+static uint32_t find_next_kv_addr(fdb_kvdb_t db, uint32_t start, uint32_t end)
+{
+    uint8_t buf[32];
+    uint32_t start_bak = start, i;
+    uint32_t magic;
+
+#ifdef FDB_KV_USING_CACHE
+    uint32_t empty_kv;
+
+    if (get_sector_from_cache(db, FDB_ALIGN_DOWN(start, db_sec_size(db)), &empty_kv) && start == empty_kv) {
+        return FAILED_ADDR;
+    }
+#endif /* FDB_KV_USING_CACHE */
+
+    for (; start < end && start + sizeof(buf) < end; start += (sizeof(buf) - sizeof(uint32_t))) {
+        if (_fdb_flash_read((fdb_db_t)db, start, (uint32_t *) buf, sizeof(buf)) != FDB_NO_ERR)
+            return FAILED_ADDR;
+        for (i = 0; i < sizeof(buf) - sizeof(uint32_t) && start + i < end; i++) {
+#ifndef FDB_BIG_ENDIAN            /* Little Endian Order */
+            magic = buf[i] + (buf[i + 1] << 8) + (buf[i + 2] << 16) + (buf[i + 3] << 24);
+#else                       /* Big Endian Order */
+            magic = buf[i + 3] + (buf[i + 2] << 8) + (buf[i + 1] << 16) + (buf[i] << 24);
+#endif
+            if (magic == KV_MAGIC_WORD && (start + i - KV_MAGIC_OFFSET) >= start_bak) {
+                return start + i - KV_MAGIC_OFFSET;
+            }
+        }
+    }
+
+    return FAILED_ADDR;
+}
+
+static uint32_t get_next_kv_addr(fdb_kvdb_t db, kv_sec_info_t sector, fdb_kv_t pre_kv)
+{
+    uint32_t addr = FAILED_ADDR;
+
+    if (sector->status.store == FDB_SECTOR_STORE_EMPTY) {
+        return FAILED_ADDR;
+    }
+
+    if (pre_kv->addr.start == FAILED_ADDR) {
+        /* the first KV address */
+        addr = sector->addr + SECTOR_HDR_DATA_SIZE;
+    } else {
+        if (pre_kv->addr.start <= sector->addr + db_sec_size(db)) {
+            if (pre_kv->crc_is_ok) {
+                addr = pre_kv->addr.start + pre_kv->len;
+            } else {
+                /* when pre_kv CRC check failed, maybe the flash has error data
+                 * find_next_kv_addr after pre_kv address */
+                addr = pre_kv->addr.start + FDB_WG_ALIGN(1);
+            }
+            /* check and find next KV address */
+            addr = find_next_kv_addr(db, addr, sector->addr + db_sec_size(db) - SECTOR_HDR_DATA_SIZE);
+
+            if (addr == FAILED_ADDR || addr > sector->addr + db_sec_size(db) || pre_kv->len == 0) {
+                //TODO Sector continuous mode
+                return FAILED_ADDR;
+            }
+        } else {
+            /* no KV */
+            return FAILED_ADDR;
+        }
+    }
+
+    return addr;
+}
+
+static fdb_err_t read_kv(fdb_kvdb_t db, fdb_kv_t kv)
+{
+    struct kv_hdr_data kv_hdr;
+    uint8_t buf[32];
+    uint32_t calc_crc32 = 0, crc_data_len, kv_name_addr;
+    fdb_err_t result = FDB_NO_ERR;
+    size_t len, size;
+    /* read KV header raw data */
+    _fdb_flash_read((fdb_db_t)db, kv->addr.start, (uint32_t *)&kv_hdr, sizeof(struct kv_hdr_data));
+    kv->status = (fdb_kv_status_t) _fdb_get_status(kv_hdr.status_table, FDB_KV_STATUS_NUM);
+    kv->len = kv_hdr.len;
+
+    if (kv->len == ~0UL || kv->len > db_max_size(db) || kv->len < KV_HDR_DATA_SIZE) {
+        /* the KV length was not write, so reserved the info for current KV */
+        kv->len = KV_HDR_DATA_SIZE;
+        if (kv->status != FDB_KV_ERR_HDR) {
+            kv->status = FDB_KV_ERR_HDR;
+            FDB_DEBUG("Error: The KV @0x%08" PRIX32 " length has an error.\n", kv->addr.start);
+            _fdb_write_status((fdb_db_t)db, kv->addr.start, kv_hdr.status_table, FDB_KV_STATUS_NUM, FDB_KV_ERR_HDR, true);
+        }
+        kv->crc_is_ok = false;
+        return FDB_READ_ERR;
+    } else if (kv->len > db_sec_size(db) - SECTOR_HDR_DATA_SIZE && kv->len < db_max_size(db)) {
+        //TODO Sector continuous mode, or the write length is not written completely
+        FDB_ASSERT(0);
+    }
+
+    /* CRC32 data len(header.name_len + header.value_len + name + value), using sizeof(uint32_t) for compatible V1.x */
+    calc_crc32 = fdb_calc_crc32(calc_crc32, &kv_hdr.name_len, sizeof(uint32_t));
+    calc_crc32 = fdb_calc_crc32(calc_crc32, &kv_hdr.value_len, sizeof(uint32_t));
+    crc_data_len = kv->len - KV_HDR_DATA_SIZE;
+    /* calculate the CRC32 value */
+    for (len = 0, size = 0; len < crc_data_len; len += size) {
+        if (len + sizeof(buf) < crc_data_len) {
+            size = sizeof(buf);
+        } else {
+            size = crc_data_len - len;
+        }
+
+        _fdb_flash_read((fdb_db_t)db, kv->addr.start + KV_HDR_DATA_SIZE + len, (uint32_t *) buf, FDB_WG_ALIGN(size));
+        calc_crc32 = fdb_calc_crc32(calc_crc32, buf, size);
+    }
+    /* check CRC32 */
+    if (calc_crc32 != kv_hdr.crc32) {
+        kv->crc_is_ok = false;
+        result = FDB_READ_ERR;
+    } else {
+        kv->crc_is_ok = true;
+        /* the name is behind aligned KV header */
+        kv_name_addr = kv->addr.start + KV_HDR_DATA_SIZE;
+        _fdb_flash_read((fdb_db_t)db, kv_name_addr, (uint32_t *) kv->name, FDB_WG_ALIGN(kv_hdr.name_len));
+        /* the value is behind aligned name */
+        kv->addr.value = kv_name_addr + FDB_WG_ALIGN(kv_hdr.name_len);
+        kv->value_len = kv_hdr.value_len;
+        kv->name_len = kv_hdr.name_len;
+        if (kv_hdr.name_len >= sizeof(kv->name) / sizeof(kv->name[0])) {
+            kv_hdr.name_len = sizeof(kv->name) / sizeof(kv->name[0]) - 1;
+        }
+        kv->name[kv_hdr.name_len] = '\0';
+    }
+
+    return result;
+}
+
+static fdb_err_t read_sector_info(fdb_kvdb_t db, uint32_t addr, kv_sec_info_t sector, bool traversal)
+{
+    fdb_err_t result = FDB_NO_ERR;
+    struct sector_hdr_data sec_hdr = { 0 };
+
+    FDB_ASSERT(addr % db_sec_size(db) == 0);
+    FDB_ASSERT(sector);
+
+    /* read sector header raw data */
+    _fdb_flash_read((fdb_db_t)db, addr, (uint32_t *)&sec_hdr, sizeof(struct sector_hdr_data));
+
+    sector->addr = addr;
+    sector->magic = sec_hdr.magic;
+    /* check magic word */
+    if (sector->magic != SECTOR_MAGIC_WORD) {
+        sector->check_ok = false;
+        sector->combined = SECTOR_NOT_COMBINED;
+        return FDB_INIT_FAILED;
+    }
+    sector->check_ok = true;
+    /* get other sector info */
+    sector->combined = sec_hdr.combined;
+    sector->status.store = (fdb_sector_store_status_t) _fdb_get_status(sec_hdr.status_table.store, FDB_SECTOR_STORE_STATUS_NUM);
+    sector->status.dirty = (fdb_sector_dirty_status_t) _fdb_get_status(sec_hdr.status_table.dirty, FDB_SECTOR_DIRTY_STATUS_NUM);
+    /* traversal all KV and calculate the remain space size */
+    if (traversal) {
+        sector->remain = 0;
+        sector->empty_kv = sector->addr + SECTOR_HDR_DATA_SIZE;
+        if (sector->status.store == FDB_SECTOR_STORE_EMPTY) {
+            sector->remain = db_sec_size(db) - SECTOR_HDR_DATA_SIZE;
+        } else if (sector->status.store == FDB_SECTOR_STORE_USING) {
+            struct fdb_kv kv_obj;
+
+#ifdef FDB_KV_USING_CACHE
+            if (get_sector_from_cache(db, addr, &sector->empty_kv)) {
+                sector->remain = db_sec_size(db) - (sector->empty_kv - sector->addr);
+                return result;
+            }
+#endif /* FDB_KV_USING_CACHE */
+
+            sector->remain = db_sec_size(db) - SECTOR_HDR_DATA_SIZE;
+            kv_obj.addr.start = sector->addr + SECTOR_HDR_DATA_SIZE;
+            do {
+
+                read_kv(db, &kv_obj);
+                if (!kv_obj.crc_is_ok) {
+                    if (kv_obj.status != FDB_KV_PRE_WRITE && kv_obj.status != FDB_KV_ERR_HDR) {
+                        FDB_INFO("Error: The KV (@0x%08" PRIX32 ") CRC32 check failed!\n", kv_obj.addr.start);
+                        sector->remain = 0;
+                        result = FDB_READ_ERR;
+                        break;
+                    }
+                }
+                sector->empty_kv += kv_obj.len;
+                sector->remain -= kv_obj.len;
+            } while ((kv_obj.addr.start = get_next_kv_addr(db, sector, &kv_obj)) != FAILED_ADDR);
+            /* check the empty KV address by read continue 0xFF on flash  */
+            {
+                uint32_t ff_addr;
+
+                ff_addr = _fdb_continue_ff_addr((fdb_db_t)db, sector->empty_kv, sector->addr + db_sec_size(db));
+                /* check the flash data is clean */
+                if (sector->empty_kv != ff_addr) {
+                    /* update the sector information */
+                    sector->empty_kv = ff_addr;
+                    sector->remain = db_sec_size(db) - (ff_addr - sector->addr);
+                }
+            }
+
+#ifdef FDB_KV_USING_CACHE
+            update_sector_cache(db, sector->addr, sector->empty_kv);
+#endif
+        }
+    }
+
+    return result;
+}
+
+static uint32_t get_next_sector_addr(fdb_kvdb_t db, kv_sec_info_t pre_sec, uint32_t traversed_len)
+{
+    uint32_t cur_block_size;
+
+    if (pre_sec->combined == SECTOR_NOT_COMBINED) {
+        cur_block_size = db_sec_size(db);
+    } else {
+        cur_block_size = pre_sec->combined * db_sec_size(db);
+    }
+
+    if (traversed_len + cur_block_size <= db_max_size(db)) {
+        /* if reach to the end, roll back to the first sector */
+        if (pre_sec->addr + cur_block_size < db_max_size(db)) {
+            return pre_sec->addr + cur_block_size;
+        } else {
+            /* the next sector is on the top of the database */
+            return 0;
+        }
+    } else {
+        /* finished */
+        return FAILED_ADDR;
+    }
+}
+
+static void kv_iterator(fdb_kvdb_t db, fdb_kv_t kv, void *arg1, void *arg2,
+        bool (*callback)(fdb_kv_t kv, void *arg1, void *arg2))
+{
+    struct kvdb_sec_info sector;
+    uint32_t sec_addr, traversed_len = 0;
+
+    sec_addr = db_oldest_addr(db);
+    /* search all sectors */
+    do {
+        traversed_len += db_sec_size(db);
+        if (read_sector_info(db, sec_addr, &sector, false) != FDB_NO_ERR) {
+            continue;
+        }
+        if (callback == NULL) {
+            continue;
+        }
+        /* sector has KV */
+        if (sector.status.store == FDB_SECTOR_STORE_USING || sector.status.store == FDB_SECTOR_STORE_FULL) {
+            kv->addr.start = sector.addr + SECTOR_HDR_DATA_SIZE;
+            /* search all KV */
+            do {
+                read_kv(db, kv);
+                /* iterator is interrupted when callback return true */
+                if (callback(kv, arg1, arg2)) {
+                    return;
+                }
+            } while ((kv->addr.start = get_next_kv_addr(db, &sector, kv)) != FAILED_ADDR);
+        }
+    } while ((sec_addr = get_next_sector_addr(db, &sector, traversed_len)) != FAILED_ADDR);
+}
+
+static bool find_kv_cb(fdb_kv_t kv, void *arg1, void *arg2)
+{
+    const char *key = arg1;
+    bool *find_ok = arg2;
+    size_t key_len = strlen(key);
+
+    if (key_len != kv->name_len) {
+        return false;
+    }
+    /* check KV */
+    if (kv->crc_is_ok && kv->status == FDB_KV_WRITE && !strncmp(kv->name, key, key_len)) {
+        *find_ok = true;
+        return true;
+    }
+    return false;
+}
+
+static bool find_kv_no_cache(fdb_kvdb_t db, const char *key, fdb_kv_t kv)
+{
+    bool find_ok = false;
+
+    kv_iterator(db, kv, (void *)key, &find_ok, find_kv_cb);
+
+    return find_ok;
+}
+
+static bool find_kv(fdb_kvdb_t db, const char *key, fdb_kv_t kv)
+{
+    bool find_ok = false;
+
+#ifdef FDB_KV_USING_CACHE
+    size_t key_len = strlen(key);
+
+    if (get_kv_from_cache(db, key, key_len, &kv->addr.start)) {
+        read_kv(db, kv);
+        return true;
+    }
+#endif /* FDB_KV_USING_CACHE */
+
+    find_ok = find_kv_no_cache(db, key, kv);
+
+#ifdef FDB_KV_USING_CACHE
+    if (find_ok) {
+        update_kv_cache(db, key, key_len, kv->addr.start);
+    }
+#endif /* FDB_KV_USING_CACHE */
+
+    return find_ok;
+}
+
+static bool fdb_is_str(uint8_t *value, size_t len)
+{
+#define __is_print(ch)       ((unsigned int)((ch) - ' ') < 127u - ' ')
+    size_t i;
+
+    for (i = 0; i < len; i++) {
+        if (!__is_print(value[i])) {
+            return false;
+        }
+    }
+    return true;
+}
+
+static size_t get_kv(fdb_kvdb_t db, const char *key, void *value_buf, size_t buf_len, size_t *value_len)
+{
+    struct fdb_kv kv;
+    size_t read_len = 0;
+
+    if (find_kv(db, key, &kv)) {
+        if (value_len) {
+            *value_len = kv.value_len;
+        }
+        if (buf_len > kv.value_len) {
+            read_len = kv.value_len;
+        } else {
+            read_len = buf_len;
+        }
+        if (value_buf){
+            _fdb_flash_read((fdb_db_t)db, kv.addr.value, (uint32_t *) value_buf, read_len);
+        }
+    } else if (value_len) {
+        *value_len = 0;
+    }
+
+    return read_len;
+}
+
+/**
+ * Get a KV object by key name
+ *
+ * @param db database object
+ * @param key KV name
+ * @param kv KV object
+ *
+ * @return KV object when is not NULL
+ */
+fdb_kv_t fdb_kv_get_obj(fdb_kvdb_t db, const char *key, fdb_kv_t kv)
+{
+    bool find_ok = false;
+
+    if (!db_init_ok(db)) {
+        FDB_INFO("Error: KV (%s) isn't initialize OK.\n", db_name(db));
+        return 0;
+    }
+
+    /* lock the KV cache */
+    db_lock(db);
+
+    find_ok = find_kv(db, key, kv);
+
+    /* unlock the KV cache */
+    db_unlock(db);
+
+    return find_ok ? kv : NULL;
+}
+
+/**
+ * Convert the KV object to blob object
+ *
+ * @param kv KV object
+ * @param blob blob object
+ *
+ * @return new blob object
+ */
+fdb_blob_t fdb_kv_to_blob(fdb_kv_t kv, fdb_blob_t blob)
+{
+    blob->saved.meta_addr = kv->addr.start;
+    blob->saved.addr = kv->addr.value;
+    blob->saved.len = kv->value_len;
+
+    return blob;
+}
+
+/**
+ * Get a blob KV value by key name.
+ *
+ * @param db database object
+ * @param key KV name
+ * @param blob blob object
+ *
+ * @return the actually get size on successful
+ */
+size_t fdb_kv_get_blob(fdb_kvdb_t db, const char *key, fdb_blob_t blob)
+{
+    size_t read_len = 0;
+
+    if (!db_init_ok(db)) {
+        FDB_INFO("Error: KV (%s) isn't initialize OK.\n", db_name(db));
+        return 0;
+    }
+
+    /* lock the KV cache */
+    db_lock(db);
+
+    read_len = get_kv(db, key, blob->buf, blob->size, &blob->saved.len);
+
+    /* unlock the KV cache */
+    db_unlock(db);
+
+    return read_len;
+}
+
+/**
+ * Get an KV value by key name.
+ *
+ * @note this function is NOT supported reentrant
+ * @note this function is DEPRECATED
+ *
+ * @param db database object
+ * @param key KV name
+ *
+ * @return value
+ */
+char *fdb_kv_get(fdb_kvdb_t db, const char *key)
+{
+    static char value[FDB_STR_KV_VALUE_MAX_SIZE + 1];
+    size_t get_size;
+    struct fdb_blob blob;
+
+    if ((get_size = fdb_kv_get_blob(db, key, fdb_blob_make(&blob, value, FDB_STR_KV_VALUE_MAX_SIZE))) > 0) {
+        /* the return value must be string */
+        if (fdb_is_str((uint8_t *)value, get_size)) {
+            value[get_size] = '\0';
+            return value;
+        } else if (blob.saved.len > FDB_STR_KV_VALUE_MAX_SIZE) {
+            FDB_INFO("Warning: The default string KV value buffer length (%" PRIdLEAST16 ") is too less (%" PRIu32 ").\n", FDB_STR_KV_VALUE_MAX_SIZE,
+                    (uint32_t)blob.saved.len);
+        } else {
+            FDB_INFO("Warning: The KV value isn't string. Could not be returned\n");
+            return NULL;
+        }
+    }
+
+    return NULL;
+}
+
+static fdb_err_t write_kv_hdr(fdb_kvdb_t db, uint32_t addr, kv_hdr_data_t kv_hdr)
+{
+    fdb_err_t result = FDB_NO_ERR;
+    /* write the status will by write granularity */
+    result = _fdb_write_status((fdb_db_t)db, addr, kv_hdr->status_table, FDB_KV_STATUS_NUM, FDB_KV_PRE_WRITE, false);
+    if (result != FDB_NO_ERR) {
+        return result;
+    }
+    /* write other header data */
+    result = _fdb_flash_write((fdb_db_t)db, addr + KV_MAGIC_OFFSET, &kv_hdr->magic, sizeof(struct kv_hdr_data) - KV_MAGIC_OFFSET, false);
+
+    return result;
+}
+
+static fdb_err_t format_sector(fdb_kvdb_t db, uint32_t addr, uint32_t combined_value)
+{
+    fdb_err_t result = FDB_NO_ERR;
+    struct sector_hdr_data sec_hdr = { 0 };
+
+    FDB_ASSERT(addr % db_sec_size(db) == 0);
+
+    result = _fdb_flash_erase((fdb_db_t)db, addr, db_sec_size(db));
+    if (result == FDB_NO_ERR) {
+        /* initialize the header data */
+        memset(&sec_hdr, FDB_BYTE_ERASED, sizeof(struct sector_hdr_data));
+#if (FDB_WRITE_GRAN == 1)
+        _fdb_set_status(sec_hdr.status_table.store, FDB_SECTOR_STORE_STATUS_NUM, FDB_SECTOR_STORE_EMPTY);
+        _fdb_set_status(sec_hdr.status_table.dirty, FDB_SECTOR_DIRTY_STATUS_NUM, FDB_SECTOR_DIRTY_FALSE);
+        sec_hdr.magic = SECTOR_MAGIC_WORD;
+        sec_hdr.combined = combined_value;
+        sec_hdr.reserved = FDB_DATA_UNUSED;
+        /* save the header */
+        result = _fdb_flash_write((fdb_db_t)db, addr, (uint32_t *)&sec_hdr, SECTOR_HDR_DATA_SIZE, true);
+#else   // seperate the whole "sec_hdr" program to serval sinle program operation to prevent re-program issue on STM32L4xx or
+        // other MCU internal flash
+        /* write the sector store status */
+        _fdb_write_status((fdb_db_t)db,
+                          addr + SECTOR_STORE_OFFSET,
+                          sec_hdr.status_table.store,
+                          FDB_SECTOR_STORE_STATUS_NUM,
+                          FDB_SECTOR_STORE_EMPTY,
+                          true);
+
+        /* write the sector dirty status */
+        _fdb_write_status((fdb_db_t)db,
+                          addr + SECTOR_DIRTY_OFFSET,
+                          sec_hdr.status_table.dirty,
+                          FDB_SECTOR_DIRTY_STATUS_NUM,
+                          FDB_SECTOR_DIRTY_FALSE,
+                          true);
+
+        /* write the magic word and combined next sector number */
+        sec_hdr.magic = SECTOR_MAGIC_WORD;
+        sec_hdr.combined = combined_value;
+        sec_hdr.reserved = FDB_DATA_UNUSED;
+        result = _fdb_flash_write((fdb_db_t)db,
+                                  addr + SECTOR_MAGIC_OFFSET,
+                                  (void *)(&(sec_hdr.magic)),
+                                  (sizeof(struct sector_hdr_data) - SECTOR_MAGIC_OFFSET),
+                                  true);
+#endif
+
+#ifdef FDB_KV_USING_CACHE
+        /* delete the sector cache */
+        update_sector_cache(db, addr, addr + db_sec_size(db));
+#endif /* FDB_KV_USING_CACHE */
+    }
+
+    return result;
+}
+
+static fdb_err_t update_sec_status(fdb_kvdb_t db, kv_sec_info_t sector, size_t new_kv_len, bool *is_full)
+{
+    uint8_t status_table[FDB_STORE_STATUS_TABLE_SIZE];
+    fdb_err_t result = FDB_NO_ERR;
+    /* change the current sector status */
+    if (sector->status.store == FDB_SECTOR_STORE_EMPTY) {
+        /* change the sector status to using */
+        result = _fdb_write_status((fdb_db_t)db, sector->addr, status_table, FDB_SECTOR_STORE_STATUS_NUM, FDB_SECTOR_STORE_USING, true);
+    } else if (sector->status.store == FDB_SECTOR_STORE_USING) {
+        /* check remain size */
+        if (sector->remain < FDB_SEC_REMAIN_THRESHOLD || sector->remain - new_kv_len < FDB_SEC_REMAIN_THRESHOLD) {
+            /* change the sector status to full */
+            result = _fdb_write_status((fdb_db_t)db, sector->addr, status_table, FDB_SECTOR_STORE_STATUS_NUM, FDB_SECTOR_STORE_FULL, true);
+
+#ifdef FDB_KV_USING_CACHE
+            /* delete the sector cache */
+            update_sector_cache(db, sector->addr, sector->addr + db_sec_size(db));
+#endif /* FDB_KV_USING_CACHE */
+
+            if (is_full) {
+                *is_full = true;
+            }
+        } else if (is_full) {
+            *is_full = false;
+        }
+    }
+
+    return result;
+}
+
+static void sector_iterator(fdb_kvdb_t db, kv_sec_info_t sector, fdb_sector_store_status_t status, void *arg1, void *arg2,
+        bool (*callback)(kv_sec_info_t sector, void *arg1, void *arg2), bool traversal_kv)
+{
+    uint32_t sec_addr, traversed_len = 0;
+
+    /* search all sectors */
+    sec_addr = db_oldest_addr(db);
+    do {
+        traversed_len += db_sec_size(db);
+        read_sector_info(db, sec_addr, sector, false);
+        if (status == FDB_SECTOR_STORE_UNUSED || status == sector->status.store) {
+            if (traversal_kv) {
+                read_sector_info(db, sec_addr, sector, true);
+            }
+            /* iterator is interrupted when callback return true */
+            if (callback && callback(sector, arg1, arg2)) {
+                return;
+            }
+        }
+    } while ((sec_addr = get_next_sector_addr(db, sector, traversed_len)) != FAILED_ADDR);
+}
+
+static bool sector_statistics_cb(kv_sec_info_t sector, void *arg1, void *arg2)
+{
+    size_t *empty_sector = arg1, *using_sector = arg2;
+
+    if (sector->check_ok && sector->status.store == FDB_SECTOR_STORE_EMPTY) {
+        (*empty_sector)++;
+    } else if (sector->check_ok && sector->status.store == FDB_SECTOR_STORE_USING) {
+        (*using_sector)++;
+    }
+
+    return false;
+}
+
+static bool alloc_kv_cb(kv_sec_info_t sector, void *arg1, void *arg2)
+{
+    struct alloc_kv_cb_args *arg = arg1;
+
+    /* 1. sector has space
+     * 2. the NO dirty sector
+     * 3. the dirty sector only when the gc_request is false */
+    if (sector->check_ok && sector->remain > arg->kv_size
+            && ((sector->status.dirty == FDB_SECTOR_DIRTY_FALSE)
+                    || (sector->status.dirty == FDB_SECTOR_DIRTY_TRUE && !arg->db->gc_request))) {
+        *(arg->empty_kv) = sector->empty_kv;
+        return true;
+    }
+
+    return false;
+}
+
+static uint32_t alloc_kv(fdb_kvdb_t db, kv_sec_info_t sector, size_t kv_size)
+{
+    uint32_t empty_kv = FAILED_ADDR;
+    size_t empty_sector = 0, using_sector = 0;
+    struct alloc_kv_cb_args arg = {db, kv_size, &empty_kv};
+
+    /* sector status statistics */
+    sector_iterator(db, sector, FDB_SECTOR_STORE_UNUSED, &empty_sector, &using_sector, sector_statistics_cb, false);
+    if (using_sector > 0) {
+        /* alloc the KV from the using status sector first */
+        sector_iterator(db, sector, FDB_SECTOR_STORE_USING, &arg, NULL, alloc_kv_cb, true);
+    }
+    if (empty_sector > 0 && empty_kv == FAILED_ADDR) {
+        if (empty_sector > FDB_GC_EMPTY_SEC_THRESHOLD || db->gc_request) {
+            sector_iterator(db, sector, FDB_SECTOR_STORE_EMPTY, &arg, NULL, alloc_kv_cb, true);
+        } else {
+            /* no space for new KV now will GC and retry */
+            FDB_DEBUG("Trigger a GC check after alloc KV failed.\n");
+            db->gc_request = true;
+        }
+    }
+
+    return empty_kv;
+}
+
+static fdb_err_t del_kv(fdb_kvdb_t db, const char *key, fdb_kv_t old_kv, bool complete_del)
+{
+    fdb_err_t result = FDB_NO_ERR;
+    uint32_t dirty_status_addr;
+    struct fdb_kv kv = { 0 };
+
+#if (KV_STATUS_TABLE_SIZE >= FDB_DIRTY_STATUS_TABLE_SIZE)
+    uint8_t status_table[KV_STATUS_TABLE_SIZE];
+#else
+    uint8_t status_table[DIRTY_STATUS_TABLE_SIZE];
+#endif
+
+    /* need find KV */
+    if (!old_kv) {
+        /* find KV */
+        if (find_kv(db, key, &kv)) {
+            old_kv = &kv;
+        } else {
+            FDB_DEBUG("Not found '%s' in KV.\n", key);
+            return FDB_KV_NAME_ERR;
+        }
+    }
+    /* change and save the new status */
+    if (!complete_del) {
+        result = _fdb_write_status((fdb_db_t)db, old_kv->addr.start, status_table, FDB_KV_STATUS_NUM, FDB_KV_PRE_DELETE, false);
+        db->last_is_complete_del = true;
+    } else {
+        result = _fdb_write_status((fdb_db_t)db, old_kv->addr.start, status_table, FDB_KV_STATUS_NUM, FDB_KV_DELETED, true);
+
+        if (!db->last_is_complete_del && result == FDB_NO_ERR) {
+#ifdef FDB_KV_USING_CACHE
+            /* delete the KV in flash and cache */
+            if (key != NULL) {
+                /* when using del_kv(db, key, NULL, true) or del_kv(db, key, kv, true) in fdb_del_kv(db, ) and set_kv(db, ) */
+                update_kv_cache(db, key, strlen(key), FDB_DATA_UNUSED);
+            } else if (old_kv != NULL) {
+                /* when using del_kv(db, NULL, kv, true) in move_kv(db, ) */
+                update_kv_cache(db, old_kv->name, old_kv->name_len, FDB_DATA_UNUSED);
+            }
+#endif /* FDB_KV_USING_CACHE */
+        }
+
+        db->last_is_complete_del = false;
+    }
+
+    dirty_status_addr = FDB_ALIGN_DOWN(old_kv->addr.start, db_sec_size(db)) + SECTOR_DIRTY_OFFSET;
+    /* read and change the sector dirty status */
+    if (result == FDB_NO_ERR
+            && _fdb_read_status((fdb_db_t)db, dirty_status_addr, status_table, FDB_SECTOR_DIRTY_STATUS_NUM) == FDB_SECTOR_DIRTY_FALSE) {
+        result = _fdb_write_status((fdb_db_t)db, dirty_status_addr, status_table, FDB_SECTOR_DIRTY_STATUS_NUM, FDB_SECTOR_DIRTY_TRUE, true);
+    }
+
+    return result;
+}
+
+/*
+ * move the KV to new space
+ */
+static fdb_err_t move_kv(fdb_kvdb_t db, fdb_kv_t kv)
+{
+    fdb_err_t result = FDB_NO_ERR;
+    uint8_t status_table[KV_STATUS_TABLE_SIZE];
+    uint32_t kv_addr;
+    struct kvdb_sec_info sector;
+
+    /* prepare to delete the current KV */
+    if (kv->status == FDB_KV_WRITE) {
+        del_kv(db, NULL, kv, false);
+    }
+
+    if ((kv_addr = alloc_kv(db, &sector, kv->len)) != FAILED_ADDR) {
+        if (db->in_recovery_check) {
+            struct fdb_kv kv_bak;
+            char name[FDB_KV_NAME_MAX + 1] = { 0 };
+            strncpy(name, kv->name, kv->name_len);
+            /* check the KV in flash is already create success */
+            if (find_kv_no_cache(db, name, &kv_bak)) {
+                /* already create success, don't need to duplicate */
+                result = FDB_NO_ERR;
+                goto __exit;
+            }
+        }
+    } else {
+        return FDB_SAVED_FULL;
+    }
+    /* start move the KV */
+    {
+        uint8_t buf[32];
+        size_t len, size, kv_len = kv->len;
+
+        /* update the new KV sector status first */
+        update_sec_status(db, &sector, kv->len, NULL);
+
+        _fdb_write_status((fdb_db_t)db, kv_addr, status_table, FDB_KV_STATUS_NUM, FDB_KV_PRE_WRITE, false);
+        kv_len -= KV_MAGIC_OFFSET;
+        for (len = 0, size = 0; len < kv_len; len += size) {
+            if (len + sizeof(buf) < kv_len) {
+                size = sizeof(buf);
+            } else {
+                size = kv_len - len;
+            }
+            _fdb_flash_read((fdb_db_t)db, kv->addr.start + KV_MAGIC_OFFSET + len, (uint32_t *) buf, FDB_WG_ALIGN(size));
+            result = _fdb_flash_write((fdb_db_t)db, kv_addr + KV_MAGIC_OFFSET + len, (uint32_t *) buf, size, true);
+        }
+        _fdb_write_status((fdb_db_t)db, kv_addr, status_table, FDB_KV_STATUS_NUM, FDB_KV_WRITE, true);
+
+#ifdef FDB_KV_USING_CACHE
+        update_sector_cache(db, FDB_ALIGN_DOWN(kv_addr, db_sec_size(db)),
+                kv_addr + KV_HDR_DATA_SIZE + FDB_WG_ALIGN(kv->name_len) + FDB_WG_ALIGN(kv->value_len));
+        update_kv_cache(db, kv->name, kv->name_len, kv_addr);
+#endif /* FDB_KV_USING_CACHE */
+    }
+
+    FDB_DEBUG("Moved the KV (%.*s) from 0x%08" PRIX32 " to 0x%08" PRIX32 ".\n", kv->name_len, kv->name, kv->addr.start, kv_addr);
+
+__exit:
+    del_kv(db, NULL, kv, true);
+
+    return result;
+}
+
+static uint32_t new_kv(fdb_kvdb_t db, kv_sec_info_t sector, size_t kv_size)
+{
+    bool already_gc = false;
+    uint32_t empty_kv = FAILED_ADDR;
+
+__retry:
+
+    if ((empty_kv = alloc_kv(db, sector, kv_size)) == FAILED_ADDR) {
+        if (db->gc_request && !already_gc) {
+            FDB_DEBUG("Warning: Alloc an KV (size %" PRIu32 ") failed when new KV. Now will GC then retry.\n", (uint32_t)kv_size);
+            gc_collect_by_free_size(db, kv_size);
+            already_gc = true;
+            goto __retry;
+        } else if (already_gc) {
+            FDB_DEBUG("Error: Alloc an KV (size %" PRIuLEAST16 ") failed after GC. KV full.\n", kv_size);
+            db->gc_request = false;
+        }
+    }
+
+    return empty_kv;
+}
+
+static uint32_t new_kv_ex(fdb_kvdb_t db, kv_sec_info_t sector, size_t key_len, size_t buf_len)
+{
+    size_t kv_len = KV_HDR_DATA_SIZE + FDB_WG_ALIGN(key_len) + FDB_WG_ALIGN(buf_len);
+
+    return new_kv(db, sector, kv_len);
+}
+
+static bool gc_check_cb(kv_sec_info_t sector, void *arg1, void *arg2)
+{
+    size_t *empty_sec = arg1;
+
+    if (sector->check_ok) {
+        *empty_sec = *empty_sec + 1;
+    }
+
+    return false;
+
+}
+
+static bool do_gc(kv_sec_info_t sector, void *arg1, void *arg2)
+{
+    struct fdb_kv kv;
+    struct gc_cb_args *gc = (struct gc_cb_args *)arg1;
+    fdb_kvdb_t db = gc->db;
+
+    gc->traversed_len += db_sec_size(db);
+    if (sector->check_ok && (sector->status.dirty == FDB_SECTOR_DIRTY_TRUE || sector->status.dirty == FDB_SECTOR_DIRTY_GC)) {
+        uint8_t status_table[FDB_DIRTY_STATUS_TABLE_SIZE];
+        /* change the sector status to GC */
+        _fdb_write_status((fdb_db_t)db, sector->addr + SECTOR_DIRTY_OFFSET, status_table, FDB_SECTOR_DIRTY_STATUS_NUM, FDB_SECTOR_DIRTY_GC, true);
+        /* search all KV */
+        kv.addr.start = sector->addr + SECTOR_HDR_DATA_SIZE;
+        do {
+            read_kv(db, &kv);
+            if (kv.crc_is_ok && (kv.status == FDB_KV_WRITE || kv.status == FDB_KV_PRE_DELETE)) {
+                /* move the KV to new space */
+                if (move_kv(db, &kv) != FDB_NO_ERR) {
+                    FDB_DEBUG("Error: Moved the KV (%.*s) for GC failed.\n", kv.name_len, kv.name);
+                }
+            }
+        } while ((kv.addr.start = get_next_kv_addr(db, sector, &kv)) != FAILED_ADDR);
+        format_sector(db, sector->addr, SECTOR_NOT_COMBINED);
+        gc->cur_free_size += db_sec_size(db) - SECTOR_HDR_DATA_SIZE;
+        FDB_DEBUG("Collect a sector @0x%08" PRIX32 "\n", sector->addr);
+        /* update oldest_addr for next GC sector format */
+        db_oldest_addr(db) = get_next_sector_addr(db, sector, gc->traversed_len);
+        if (gc->cur_free_size >= gc->setting_free_size)
+            return true;
+    }
+
+    return false;
+}
+
+static void gc_collect_by_free_size(fdb_kvdb_t db, size_t free_size)
+{
+    struct kvdb_sec_info sector;
+    size_t empty_sec = 0;
+    struct gc_cb_args arg = { db, 0, free_size, 0 };
+
+    /* GC check the empty sector number */
+    sector_iterator(db, &sector, FDB_SECTOR_STORE_EMPTY, &empty_sec, NULL, gc_check_cb, false);
+
+    /* do GC collect */
+    FDB_DEBUG("The remain empty sector is %" PRIu32 ", GC threshold is %" PRIdLEAST16 ".\n", (uint32_t)empty_sec, FDB_GC_EMPTY_SEC_THRESHOLD);
+    if (empty_sec <= FDB_GC_EMPTY_SEC_THRESHOLD) {
+        sector_iterator(db, &sector, FDB_SECTOR_STORE_UNUSED, &arg, NULL, do_gc, false);
+    }
+
+    db->gc_request = false;
+}
+
+/*
+ * The GC will be triggered on the following scene:
+ * 1. alloc an KV when the flash not has enough space
+ * 2. write an KV then the flash not has enough space
+ */
+static void gc_collect(fdb_kvdb_t db)
+{
+    gc_collect_by_free_size(db, db_max_size(db));
+}
+
+static fdb_err_t align_write(fdb_kvdb_t db, uint32_t addr, const uint32_t *buf, size_t size)
+{
+    fdb_err_t result = FDB_NO_ERR;
+    size_t align_remain;
+
+#if (FDB_WRITE_GRAN / 8 > 0)
+    uint8_t align_data[FDB_WRITE_GRAN / 8];
+    size_t align_data_size = sizeof(align_data);
+#else
+    /* For compatibility with C89 */
+    uint8_t align_data_u8, *align_data = &align_data_u8;
+    size_t align_data_size = 1;
+#endif
+
+    memset(align_data, FDB_BYTE_ERASED, align_data_size);
+    result = _fdb_flash_write((fdb_db_t) db, addr, buf, FDB_WG_ALIGN_DOWN(size), false);
+
+    align_remain = size - FDB_WG_ALIGN_DOWN(size);
+    if (result == FDB_NO_ERR && align_remain) {
+        memcpy(align_data, (uint8_t *) buf + FDB_WG_ALIGN_DOWN(size), align_remain);
+        result = _fdb_flash_write((fdb_db_t) db, addr + FDB_WG_ALIGN_DOWN(size), (uint32_t *) align_data,
+                align_data_size, false);
+    }
+
+    return result;
+}
+
+static fdb_err_t create_kv_blob(fdb_kvdb_t db, kv_sec_info_t sector, const char *key, const void *value, size_t len)
+{
+    fdb_err_t result = FDB_NO_ERR;
+    struct kv_hdr_data kv_hdr;
+    bool is_full = false;
+    uint32_t kv_addr = sector->empty_kv;
+
+    if (strlen(key) > FDB_KV_NAME_MAX) {
+        FDB_INFO("Error: The KV name length is more than %d\n", FDB_KV_NAME_MAX);
+        return FDB_KV_NAME_ERR;
+    }
+
+    memset(&kv_hdr, FDB_BYTE_ERASED, sizeof(struct kv_hdr_data));
+    kv_hdr.magic = KV_MAGIC_WORD;
+    kv_hdr.name_len = strlen(key);
+    kv_hdr.value_len = len;
+    kv_hdr.len = KV_HDR_DATA_SIZE + FDB_WG_ALIGN(kv_hdr.name_len) + FDB_WG_ALIGN(kv_hdr.value_len);
+
+    if (kv_hdr.len > db_sec_size(db) - SECTOR_HDR_DATA_SIZE) {
+        FDB_INFO("Error: The KV size is too big\n");
+        return FDB_SAVED_FULL;
+    }
+
+    if (kv_addr != FAILED_ADDR || (kv_addr = new_kv(db, sector, kv_hdr.len)) != FAILED_ADDR) {
+        size_t align_remain;
+        /* update the sector status */
+        if (result == FDB_NO_ERR) {
+            result = update_sec_status(db, sector, kv_hdr.len, &is_full);
+        }
+        if (result == FDB_NO_ERR) {
+            uint8_t ff = FDB_BYTE_ERASED;
+            /* start calculate CRC32 */
+            kv_hdr.crc32 = 0;
+            /* CRC32(header.name_len + header.value_len + name + value), using sizeof(uint32_t) for compatible V1.x */
+            kv_hdr.crc32 = fdb_calc_crc32(kv_hdr.crc32, &kv_hdr.name_len, sizeof(uint32_t));
+            kv_hdr.crc32 = fdb_calc_crc32(kv_hdr.crc32, &kv_hdr.value_len, sizeof(uint32_t));
+            kv_hdr.crc32 = fdb_calc_crc32(kv_hdr.crc32, key, kv_hdr.name_len);
+            align_remain = FDB_WG_ALIGN(kv_hdr.name_len) - kv_hdr.name_len;
+            while (align_remain--) {
+                kv_hdr.crc32 = fdb_calc_crc32(kv_hdr.crc32, &ff, 1);
+            }
+            kv_hdr.crc32 = fdb_calc_crc32(kv_hdr.crc32, value, kv_hdr.value_len);
+            align_remain = FDB_WG_ALIGN(kv_hdr.value_len) - kv_hdr.value_len;
+            while (align_remain--) {
+                kv_hdr.crc32 = fdb_calc_crc32(kv_hdr.crc32, &ff, 1);
+            }
+            /* write KV header data */
+            result = write_kv_hdr(db, kv_addr, &kv_hdr);
+        }
+        /* write key name */
+        if (result == FDB_NO_ERR) {
+            result = align_write(db, kv_addr + KV_HDR_DATA_SIZE, (uint32_t *) key, kv_hdr.name_len);
+
+#ifdef FDB_KV_USING_CACHE
+            if (!is_full) {
+                update_sector_cache(db, sector->addr,
+                        kv_addr + KV_HDR_DATA_SIZE + FDB_WG_ALIGN(kv_hdr.name_len) + FDB_WG_ALIGN(kv_hdr.value_len));
+            }
+            update_kv_cache(db, key, kv_hdr.name_len, kv_addr);
+#endif /* FDB_KV_USING_CACHE */
+        }
+        /* write value */
+        if (result == FDB_NO_ERR) {
+            result = align_write(db, kv_addr + KV_HDR_DATA_SIZE + FDB_WG_ALIGN(kv_hdr.name_len), value,
+                    kv_hdr.value_len);
+        }
+        /* change the KV status to KV_WRITE */
+        if (result == FDB_NO_ERR) {
+            result = _fdb_write_status((fdb_db_t) db, kv_addr, kv_hdr.status_table, FDB_KV_STATUS_NUM, FDB_KV_WRITE,
+                    true);
+        }
+        /* trigger GC collect when current sector is full */
+        if (result == FDB_NO_ERR && is_full) {
+            FDB_DEBUG("Trigger a GC check after created KV.\n");
+            db->gc_request = true;
+        }
+    } else {
+        result = FDB_SAVED_FULL;
+    }
+
+    return result;
+}
+
+/**
+ * Delete an KV.
+ *
+ * @param db database object
+ * @param key KV name
+ *
+ * @return result
+ */
+fdb_err_t fdb_kv_del(fdb_kvdb_t db, const char *key)
+{
+    fdb_err_t result = FDB_NO_ERR;
+
+    if (!db_init_ok(db)) {
+        FDB_INFO("Error: KV (%s) isn't initialize OK.\n", db_name(db));
+        return FDB_INIT_FAILED;
+    }
+
+    /* lock the KV cache */
+    db_lock(db);
+
+    result = del_kv(db, key, NULL, true);
+
+    /* unlock the KV cache */
+    db_unlock(db);
+
+    return result;
+}
+
+static fdb_err_t set_kv(fdb_kvdb_t db, const char *key, const void *value_buf, size_t buf_len)
+{
+    fdb_err_t result = FDB_NO_ERR;
+    bool kv_is_found = false;
+
+    if (value_buf == NULL) {
+        result = del_kv(db, key, NULL, true);
+    } else {
+        /* make sure the flash has enough space */
+        if (new_kv_ex(db, &db->cur_sector, strlen(key), buf_len) == FAILED_ADDR) {
+            return FDB_SAVED_FULL;
+        }
+        kv_is_found = find_kv(db, key, &db->cur_kv);
+        /* prepare to delete the old KV */
+        if (kv_is_found) {
+            result = del_kv(db, key, &db->cur_kv, false);
+        }
+        /* create the new KV */
+        if (result == FDB_NO_ERR) {
+            result = create_kv_blob(db, &db->cur_sector, key, value_buf, buf_len);
+        }
+        /* delete the old KV */
+        if (kv_is_found && result == FDB_NO_ERR) {
+            result = del_kv(db, key, &db->cur_kv, true);
+        }
+        /* process the GC after set KV */
+        if (db->gc_request) {
+            gc_collect_by_free_size(db, KV_HDR_DATA_SIZE + FDB_WG_ALIGN(strlen(key)) + FDB_WG_ALIGN(buf_len));
+        }
+    }
+
+    return result;
+}
+
+/**
+ * Set a blob KV. If it blob value is NULL, delete it.
+ * If not find it in flash, then create it.
+ *
+ * @param db database object
+ * @param key KV name
+ * @param blob blob object
+ *
+ * @return result
+ */
+fdb_err_t fdb_kv_set_blob(fdb_kvdb_t db, const char *key, fdb_blob_t blob)
+{
+    fdb_err_t result = FDB_NO_ERR;
+
+    if (!db_init_ok(db)) {
+        FDB_INFO("Error: KV (%s) isn't initialize OK.\n", db_name(db));
+        return FDB_INIT_FAILED;
+    }
+
+    /* lock the KV cache */
+    db_lock(db);
+
+    result = set_kv(db, key, blob->buf, blob->size);
+
+    /* unlock the KV cache */
+    db_unlock(db);
+
+    return result;
+}
+
+/**
+ * Set a string KV. If it value is NULL, delete it.
+ * If not find it in flash, then create it.
+ *
+ * @param db database object
+ * @param key KV name
+ * @param value KV value
+ *
+ * @return result
+ */
+fdb_err_t fdb_kv_set(fdb_kvdb_t db, const char *key, const char *value)
+{
+    struct fdb_blob blob;
+
+    return fdb_kv_set_blob(db, key, fdb_blob_make(&blob, value, strlen(value)));
+}
+
+/**
+ * recovery all KV to default.
+ *
+ * @param db database object
+ * @return result
+ */
+fdb_err_t fdb_kv_set_default(fdb_kvdb_t db)
+{
+    fdb_err_t result = FDB_NO_ERR;
+    uint32_t addr, i, value_len;
+    struct kvdb_sec_info sector;
+
+    /* lock the KV cache */
+    db_lock(db);
+    /* format all sectors */
+    for (addr = 0; addr < db_max_size(db); addr += db_sec_size(db)) {
+        result = format_sector(db, addr, SECTOR_NOT_COMBINED);
+        if (result != FDB_NO_ERR) {
+            goto __exit;
+        }
+    }
+    /* create default KV */
+    for (i = 0; i < db->default_kvs.num; i++) {
+        /* It seems to be a string when value length is 0.
+         * This mechanism is for compatibility with older versions (less then V4.0). */
+        if (db->default_kvs.kvs[i].value_len == 0) {
+            value_len = strlen(db->default_kvs.kvs[i].value);
+        } else {
+            value_len = db->default_kvs.kvs[i].value_len;
+        }
+        sector.empty_kv = FAILED_ADDR;
+        create_kv_blob(db, &sector, db->default_kvs.kvs[i].key, db->default_kvs.kvs[i].value, value_len);
+        if (result != FDB_NO_ERR) {
+            goto __exit;
+        }
+    }
+
+__exit:
+    db_oldest_addr(db) = 0;
+    /* unlock the KV cache */
+    db_unlock(db);
+
+    return result;
+}
+
+static bool print_kv_cb(fdb_kv_t kv, void *arg1, void *arg2)
+{
+    bool value_is_str = true, print_value = false;
+    size_t *using_size = arg1;
+    fdb_kvdb_t db = arg2;
+
+    if (kv->crc_is_ok) {
+        /* calculate the total using flash size */
+        *using_size += kv->len;
+        /* check KV */
+        if (kv->status == FDB_KV_WRITE) {
+            FDB_PRINT("%.*s=", kv->name_len, kv->name);
+
+            if (kv->value_len < FDB_STR_KV_VALUE_MAX_SIZE ) {
+                uint8_t buf[32];
+                size_t len, size;
+__reload:
+                /* check the value is string */
+                for (len = 0, size = 0; len < kv->value_len; len += size) {
+                    if (len + sizeof(buf) < kv->value_len) {
+                        size = sizeof(buf);
+                    } else {
+                        size = kv->value_len - len;
+                    }
+                    _fdb_flash_read((fdb_db_t)db, kv->addr.value + len, (uint32_t *) buf, FDB_WG_ALIGN(size));
+                    if (print_value) {
+                        FDB_PRINT("%.*s", (int)size, buf);
+                    } else if (!fdb_is_str(buf, size)) {
+                        value_is_str = false;
+                        break;
+                    }
+                }
+            } else {
+                value_is_str = false;
+            }
+            if (value_is_str && !print_value) {
+                print_value = true;
+                goto __reload;
+            } else if (!value_is_str) {
+                FDB_PRINT("blob @0x%08" PRIX32 " %" PRIu32 "bytes", kv->addr.value, kv->value_len);
+            }
+            FDB_PRINT("\n");
+        }
+    }
+
+    return false;
+}
+
+
+/**
+ * Print all KV.
+ *
+ * @param db database object
+ */
+void fdb_kv_print(fdb_kvdb_t db)
+{
+    struct fdb_kv kv;
+    size_t using_size = 0;
+
+    if (!db_init_ok(db)) {
+        FDB_INFO("Error: KV (%s) isn't initialize OK.\n", db_name(db));
+        return;
+    }
+
+    /* lock the KV cache */
+    db_lock(db);
+
+    kv_iterator(db, &kv, &using_size, db, print_kv_cb);
+
+    FDB_PRINT("\nmode: next generation\n");
+    FDB_PRINT("size: %" PRIu32 "/%" PRIu32 " bytes.\n", (uint32_t)using_size + ((SECTOR_NUM - FDB_GC_EMPTY_SEC_THRESHOLD) * SECTOR_HDR_DATA_SIZE),
+            db_max_size(db) - db_sec_size(db) * FDB_GC_EMPTY_SEC_THRESHOLD);
+
+    /* unlock the KV cache */
+    db_unlock(db);
+}
+
+#ifdef FDB_KV_AUTO_UPDATE
+/*
+ * Auto update KV to latest default when current setting version number is changed.
+ */
+static void kv_auto_update(fdb_kvdb_t db)
+{
+    size_t saved_ver_num, setting_ver_num = db->ver_num;
+
+    if (get_kv(db, VER_NUM_KV_NAME, &saved_ver_num, sizeof(size_t), NULL) > 0) {
+        /* check version number */
+        if (saved_ver_num != setting_ver_num) {
+            size_t i, value_len;
+            FDB_DEBUG("Update the KV from version %zu to %zu.\n", saved_ver_num, setting_ver_num);
+            for (i = 0; i < db->default_kvs.num; i++) {
+                /* add a new KV when it's not found */
+                if (!find_kv(db, db->default_kvs.kvs[i].key, &db->cur_kv)) {
+                    /* It seems to be a string when value length is 0.
+                     * This mechanism is for compatibility with older versions (less then V4.0). */
+                    if (db->default_kvs.kvs[i].value_len == 0) {
+                        value_len = strlen(db->default_kvs.kvs[i].value);
+                    } else {
+                        value_len = db->default_kvs.kvs[i].value_len;
+                    }
+                    db->cur_sector.empty_kv = FAILED_ADDR;
+                    create_kv_blob(db, &db->cur_sector, db->default_kvs.kvs[i].key, db->default_kvs.kvs[i].value, value_len);
+                }
+            }
+        } else {
+            /* version number not changed now return */
+            return;
+        }
+    }
+
+    set_kv(db, VER_NUM_KV_NAME, &setting_ver_num, sizeof(size_t));
+}
+#endif /* FDB_KV_AUTO_UPDATE */
+
+static bool check_oldest_addr_cb(kv_sec_info_t sector, void *arg1, void *arg2)
+{
+    uint32_t *sector_oldest_addr = (uint32_t *) arg1;
+    fdb_sector_store_status_t *last_sector_status = (fdb_sector_store_status_t *)arg2;
+
+    /* The oldest address is 0 by default.
+     * The new oldest sector is found when sector status change from empty to full or using.
+     */
+    if (*last_sector_status == FDB_SECTOR_STORE_EMPTY
+            && (sector->status.store == FDB_SECTOR_STORE_FULL || sector->status.store == FDB_SECTOR_STORE_USING)) {
+        *sector_oldest_addr = sector->addr;
+    }
+
+    *last_sector_status = sector->status.store;
+    return false;
+}
+
+static bool check_sec_hdr_cb(kv_sec_info_t sector, void *arg1, void *arg2)
+{
+    if (!sector->check_ok) {
+        size_t *failed_count = arg1;
+        fdb_kvdb_t db = arg2;
+
+        (*failed_count) ++;
+        if (db->parent.not_formatable) {
+            return true;
+        } else {
+            FDB_DEBUG("Sector header info is incorrect. Auto format this sector (0x%08" PRIX32 ").\n", sector->addr);
+            format_sector(db, sector->addr, SECTOR_NOT_COMBINED);
+        }
+    }
+
+    return false;
+}
+
+static bool check_and_recovery_gc_cb(kv_sec_info_t sector, void *arg1, void *arg2)
+{
+    fdb_kvdb_t db = arg1;
+
+    if (sector->check_ok && sector->status.dirty == FDB_SECTOR_DIRTY_GC) {
+        /* make sure the GC request flag to true */
+        db->gc_request = true;
+        /* resume the GC operate */
+        gc_collect(db);
+    }
+
+    return false;
+}
+
+static bool check_and_recovery_kv_cb(fdb_kv_t kv, void *arg1, void *arg2)
+{
+    fdb_kvdb_t db = arg1;
+
+    /* recovery the prepare deleted KV */
+    if (kv->crc_is_ok && kv->status == FDB_KV_PRE_DELETE) {
+        FDB_INFO("Found an KV (%.*s) which has changed value failed. Now will recovery it.\n", kv->name_len, kv->name);
+        /* recovery the old KV */
+        if (move_kv(db, kv) == FDB_NO_ERR) {
+            FDB_DEBUG("Recovery the KV successful.\n");
+        } else {
+            FDB_DEBUG("Warning: Moved an KV (size %" PRIu32 ") failed when recovery. Now will GC then retry.\n", kv->len);
+            return true;
+        }
+    } else if (kv->status == FDB_KV_PRE_WRITE) {
+        uint8_t status_table[KV_STATUS_TABLE_SIZE];
+        /* the KV has not write finish, change the status to error */
+        //TODO Draw the state replacement diagram of exception handling
+        _fdb_write_status((fdb_db_t)db, kv->addr.start, status_table, FDB_KV_STATUS_NUM, FDB_KV_ERR_HDR, true);
+        return true;
+    } else if (kv->crc_is_ok && kv->status == FDB_KV_WRITE) {
+#ifdef FDB_KV_USING_CACHE
+        /* update the cache when first load. If caching is disabled, this step is not performed */
+        update_kv_cache(db, kv->name, kv->name_len, kv->addr.start);
+#endif
+    }
+
+    return false;
+}
+
+/**
+ * Check and load the flash KV.
+ *
+ * @return result
+ */
+fdb_err_t _fdb_kv_load(fdb_kvdb_t db)
+{
+    fdb_err_t result = FDB_NO_ERR;
+    struct fdb_kv kv;
+    struct kvdb_sec_info sector;
+    size_t check_failed_count = 0;
+
+    db->in_recovery_check = true;
+    /* check all sector header */
+    sector_iterator(db, &sector, FDB_SECTOR_STORE_UNUSED, &check_failed_count, db, check_sec_hdr_cb, false);
+    if (db->parent.not_formatable && check_failed_count > 0) {
+        result = FDB_READ_ERR;
+        goto __exit;
+    }
+    /* all sector header check failed */
+    if (check_failed_count == SECTOR_NUM) {
+        FDB_INFO("All sector header is incorrect. Set it to default.\n");
+        fdb_kv_set_default(db);
+    }
+
+    /* lock the KV cache */
+    db_lock(db);
+    /* check all sector header for recovery GC */
+    sector_iterator(db, &sector, FDB_SECTOR_STORE_UNUSED, db, NULL, check_and_recovery_gc_cb, false);
+
+__retry:
+    /* check all KV for recovery */
+    kv_iterator(db, &kv, db, NULL, check_and_recovery_kv_cb);
+    if (db->gc_request) {
+        gc_collect(db);
+        goto __retry;
+    }
+
+    db->in_recovery_check = false;
+
+__exit:
+    /* unlock the KV cache */
+    db_unlock(db);
+
+    return result;
+}
+
+/**
+ * This function will get or set some options of the database
+ *
+ * @param db database object
+ * @param cmd the control command
+ * @param arg the argument
+ */
+void fdb_kvdb_control(fdb_kvdb_t db, int cmd, void *arg)
+{
+    FDB_ASSERT(db);
+
+    switch (cmd) {
+    case FDB_KVDB_CTRL_SET_SEC_SIZE:
+        /* this change MUST before database initialization */
+        FDB_ASSERT(db->parent.init_ok == false);
+        db->parent.sec_size = *(uint32_t *) arg;
+        break;
+    case FDB_KVDB_CTRL_GET_SEC_SIZE:
+        *(uint32_t *) arg = db->parent.sec_size;
+        break;
+    case FDB_KVDB_CTRL_SET_LOCK:
+#if !defined(__ARMCC_VERSION) && defined(__GNUC__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wpedantic"
+#endif
+        db->parent.lock = (void (*)(fdb_db_t db)) arg;
+#if !defined(__ARMCC_VERSION) && defined(__GNUC__)
+#pragma GCC diagnostic pop
+#endif
+        break;
+    case FDB_KVDB_CTRL_SET_UNLOCK:
+#if !defined(__ARMCC_VERSION) && defined(__GNUC__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wpedantic"
+#endif
+        db->parent.unlock = (void (*)(fdb_db_t db)) arg;
+#if !defined(__ARMCC_VERSION) && defined(__GNUC__)
+#pragma GCC diagnostic pop
+#endif
+        break;
+    case FDB_KVDB_CTRL_SET_FILE_MODE:
+#ifdef FDB_USING_FILE_MODE
+        /* this change MUST before database initialization */
+        FDB_ASSERT(db->parent.init_ok == false);
+        db->parent.file_mode = *(bool *) arg;
+#else
+        FDB_INFO("Error: set file mode Failed. Please defined the FDB_USING_FILE_MODE macro.");
+#endif
+        break;
+    case FDB_KVDB_CTRL_SET_MAX_SIZE:
+#ifdef FDB_USING_FILE_MODE
+        /* this change MUST before database initialization */
+        FDB_ASSERT(db->parent.init_ok == false);
+        db->parent.max_size = *(uint32_t *)arg;
+#endif
+        break;
+    case FDB_KVDB_CTRL_SET_NOT_FORMAT:
+        /* this change MUST before database initialization */
+        FDB_ASSERT(db->parent.init_ok == false);
+        db->parent.not_formatable = *(bool *)arg;
+        break;
+    }
+}
+
+/**
+ * The KV database initialization.
+ *
+ * @param db database object
+ * @param name database name
+ * @param path FAL mode: partition name, file mode: database saved directory path
+ * @param default_kv the default KV set @see fdb_default_kv
+ * @param user_data user data
+ *
+ * @return result
+ */
+fdb_err_t fdb_kvdb_init(fdb_kvdb_t db, const char *name, const char *path, struct fdb_default_kv *default_kv,
+        void *user_data)
+{
+    fdb_err_t result = FDB_NO_ERR;
+    struct kvdb_sec_info sector;
+
+#ifdef FDB_KV_USING_CACHE
+    size_t i;
+#endif
+
+    /* must be aligned with write granularity */
+    FDB_ASSERT((FDB_STR_KV_VALUE_MAX_SIZE * 8) % FDB_WRITE_GRAN == 0);
+
+    result = _fdb_init_ex((fdb_db_t) db, name, path, FDB_DB_TYPE_KV, user_data);
+    if (result != FDB_NO_ERR) {
+        goto __exit;
+    }
+
+    db->gc_request = false;
+    db->in_recovery_check = false;
+    if (default_kv) {
+        db->default_kvs = *default_kv;
+    } else {
+        db->default_kvs.num = 0;
+        db->default_kvs.kvs = NULL;
+    }
+
+    { /* find the oldest sector address */
+        uint32_t sector_oldest_addr = 0;
+        fdb_sector_store_status_t last_sector_status = FDB_SECTOR_STORE_UNUSED;
+
+        db_oldest_addr(db) = 0;
+        sector_iterator(db, &sector, FDB_SECTOR_STORE_UNUSED, &sector_oldest_addr, &last_sector_status,
+                check_oldest_addr_cb, false);
+        db_oldest_addr(db) = sector_oldest_addr;
+        FDB_DEBUG("The oldest addr is @0x%08" PRIX32 "\n", db_oldest_addr(db));
+    }
+    /* there is at least one empty sector for GC. */
+    FDB_ASSERT((FDB_GC_EMPTY_SEC_THRESHOLD > 0 && FDB_GC_EMPTY_SEC_THRESHOLD < SECTOR_NUM))
+
+#ifdef FDB_KV_USING_CACHE
+    for (i = 0; i < FDB_SECTOR_CACHE_TABLE_SIZE; i++) {
+        db->sector_cache_table[i].addr = FDB_DATA_UNUSED;
+    }
+    for (i = 0; i < FDB_KV_CACHE_TABLE_SIZE; i++) {
+        db->kv_cache_table[i].addr = FDB_DATA_UNUSED;
+    }
+#endif /* FDB_KV_USING_CACHE */
+
+    FDB_DEBUG("KVDB size is %" PRIu32 " bytes.\n", db_max_size(db));
+
+    result = _fdb_kv_load(db);
+
+#ifdef FDB_KV_AUTO_UPDATE
+    if (result == FDB_NO_ERR) {
+        kv_auto_update(db);
+    }
+#endif
+
+__exit:
+
+    _fdb_init_finish((fdb_db_t)db, result);
+
+    return result;
+}
+
+/**
+ * The KV database initialization.
+ *
+ * @param db database object
+ *
+ * @return result
+ */
+fdb_err_t fdb_kvdb_deinit(fdb_kvdb_t db)
+{
+    _fdb_deinit((fdb_db_t) db);
+
+    return FDB_NO_ERR;
+}
+
+/**
+ * The KV database initialization.
+ *
+ * @param db database object
+ * @param itr iterator structure to be initialized
+ *
+ * @return pointer to the iterator initialized.
+ */
+fdb_kv_iterator_t fdb_kv_iterator_init(fdb_kvdb_t db, fdb_kv_iterator_t itr)
+{
+    itr->curr_kv.addr.start = 0;
+
+    /* If iterator statistics is needed */
+    itr->iterated_cnt = 0;
+    itr->iterated_obj_bytes = 0;
+    itr->iterated_value_bytes = 0;
+    itr->traversed_len = 0;
+    /* Start from sector head */
+    itr->sector_addr = db_oldest_addr(db);
+    return itr;
+}
+
+/**
+ * The KV database iterator.
+ *
+ * @param db database object
+ * @param itr the iterator structure
+ *
+ * @return false if iteration is ended, true if iteration is not ended.
+ */
+bool fdb_kv_iterate(fdb_kvdb_t db, fdb_kv_iterator_t itr)
+{
+    struct kvdb_sec_info sector;
+    fdb_kv_t kv = &(itr->curr_kv);
+
+    do {
+        if (read_sector_info(db, itr->sector_addr, &sector, false) == FDB_NO_ERR) {
+            if (sector.status.store == FDB_SECTOR_STORE_USING || sector.status.store == FDB_SECTOR_STORE_FULL) {
+                if (kv->addr.start == 0) {
+                    kv->addr.start = sector.addr + SECTOR_HDR_DATA_SIZE;
+                } else if ((kv->addr.start = get_next_kv_addr(db, &sector, kv)) == FAILED_ADDR) {
+                    kv->addr.start = 0;
+                    itr->traversed_len += db_sec_size(db);
+                    continue;
+                }
+                do {
+                    read_kv(db, kv);
+                    if (kv->status == FDB_KV_WRITE) {
+                        /* We got a valid kv here. */
+                        /* If iterator statistics is needed */
+                        itr->iterated_cnt++;
+                        itr->iterated_obj_bytes += kv->len;
+                        itr->iterated_value_bytes += kv->value_len;
+                        return true;
+                    }
+                } while ((kv->addr.start = get_next_kv_addr(db, &sector, kv)) != FAILED_ADDR);
+            }
+        }
+        /** Set kv->addr.start to 0 when we get into a new sector so that if we successfully get the next sector info,
+         *  the kv->addr.start is set to the new sector.addr + SECTOR_HDR_DATA_SIZE.
+         */
+        kv->addr.start = 0;
+        itr->traversed_len += db_sec_size(db);
+    } while ((itr->sector_addr = get_next_sector_addr(db, &sector, itr->traversed_len)) != FAILED_ADDR);
+    /* Finally we have iterated all the KVs. */
+    return false;
+}
+
+#endif /* defined(FDB_USING_KVDB) */

+ 67 - 0
package/flashdb/fdb_low_lvl.h

@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2020, Armink, <armink.ztl@gmail.com>
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/**
+ * @file
+ * @brief low level API and definition
+ */
+
+#ifndef _FDB_LOW_LVL_H_
+#define _FDB_LOW_LVL_H_
+
+#include "fdb_cfg.h"
+#include "fdb_def.h"
+
+#if (FDB_WRITE_GRAN == 1)
+#define FDB_STATUS_TABLE_SIZE(status_number)       ((status_number * FDB_WRITE_GRAN + 7)/8)
+#else
+#define FDB_STATUS_TABLE_SIZE(status_number)       (((status_number - 1) * FDB_WRITE_GRAN + 7)/8)
+#endif
+
+/* the data is erased */
+#define FDB_BYTE_ERASED                           0xFF
+/* the data is written */
+#define FDB_BYTE_WRITTEN                          0x00
+
+/* Return the most contiguous size aligned at specified width. RT_ALIGN(13, 4)
+ * would return 16.
+ */
+#define FDB_ALIGN(size, align)                    (((size) + (align) - 1) & ~((align) - 1))
+/* align by write granularity */
+#define FDB_WG_ALIGN(size)                        (FDB_ALIGN(size, (FDB_WRITE_GRAN + 7)/8))
+/**
+ * Return the down number of aligned at specified width. RT_ALIGN_DOWN(13, 4)
+ * would return 12.
+ */
+#define FDB_ALIGN_DOWN(size, align)               ((size) & ~((align) - 1))
+/* align down by write granularity */
+#define FDB_WG_ALIGN_DOWN(size)                   (FDB_ALIGN_DOWN(size, (FDB_WRITE_GRAN + 7)/8))
+
+#define FDB_STORE_STATUS_TABLE_SIZE               FDB_STATUS_TABLE_SIZE(FDB_SECTOR_STORE_STATUS_NUM)
+#define FDB_DIRTY_STATUS_TABLE_SIZE               FDB_STATUS_TABLE_SIZE(FDB_SECTOR_DIRTY_STATUS_NUM)
+
+/* the data is unused */
+#if (FDB_BYTE_ERASED  == 0xFF)
+#define FDB_DATA_UNUSED                      0xFFFFFFFF
+#else
+#define FDB_DATA_UNUSED                      0x00000000
+#endif
+
+fdb_err_t _fdb_kv_load(fdb_kvdb_t db);
+size_t _fdb_set_status(uint8_t status_table[], size_t status_num, size_t status_index);
+size_t _fdb_get_status(uint8_t status_table[], size_t status_num);
+uint32_t _fdb_continue_ff_addr(fdb_db_t db, uint32_t start, uint32_t end);
+fdb_err_t _fdb_init_ex(fdb_db_t db, const char *name, const char *part_name, fdb_db_type type, void *user_data);
+void _fdb_init_finish(fdb_db_t db, fdb_err_t result);
+void _fdb_deinit(fdb_db_t db);
+const char *_fdb_db_path(fdb_db_t db);
+fdb_err_t _fdb_write_status(fdb_db_t db, uint32_t addr, uint8_t status_table[], size_t status_num, size_t status_index, bool sync);
+size_t _fdb_read_status(fdb_db_t db, uint32_t addr, uint8_t status_table[], size_t total_num);
+fdb_err_t _fdb_flash_read(fdb_db_t db, uint32_t addr, void *buf, size_t size);
+fdb_err_t _fdb_flash_erase(fdb_db_t db, uint32_t addr, size_t size);
+fdb_err_t _fdb_flash_write(fdb_db_t db, uint32_t addr, const void *buf, size_t size, bool sync);
+
+#endif /* _FDB_LOW_LVL_H_ */

+ 985 - 0
package/flashdb/fdb_tsdb.c

@@ -0,0 +1,985 @@
+/*
+ * Copyright (c) 2020, Armink, <armink.ztl@gmail.com>
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/**
+ * @file
+ * @brief TSDB feature.
+ *
+ * Time series log (like TSDB) feature implement source file.
+ *
+ * TSL is time series log, the TSDB saved many TSLs.
+ */
+
+#include <inttypes.h>
+#include <string.h>
+#include "flashdb.h"
+#include "fdb_low_lvl.h"
+
+#define FDB_LOG_TAG "[tsl]"
+/* rewrite log prefix */
+#undef  FDB_LOG_PREFIX2
+#define FDB_LOG_PREFIX2()                         FDB_PRINT("[%s][%s] ", db_name(db), _fdb_db_path((fdb_db_t)db))
+
+#if defined(FDB_USING_TSDB)
+
+#if (FDB_WRITE_GRAN == 64)
+#error "Flash 64 bits write granularity is not supported in TSDB yet!"
+#endif
+
+/* magic word(`T`, `S`, `L`, `0`) */
+#define SECTOR_MAGIC_WORD                        0x304C5354
+
+#define TSL_STATUS_TABLE_SIZE                    FDB_STATUS_TABLE_SIZE(FDB_TSL_STATUS_NUM)
+
+#define SECTOR_HDR_DATA_SIZE                     (FDB_WG_ALIGN(sizeof(struct sector_hdr_data)))
+#define LOG_IDX_DATA_SIZE                        (FDB_WG_ALIGN(sizeof(struct log_idx_data)))
+#define LOG_IDX_TS_OFFSET                        ((unsigned long)(&((struct log_idx_data *)0)->time))
+#define SECTOR_MAGIC_OFFSET                      ((unsigned long)(&((struct sector_hdr_data *)0)->magic))
+#define SECTOR_START_TIME_OFFSET                 ((unsigned long)(&((struct sector_hdr_data *)0)->start_time))
+#define SECTOR_END0_TIME_OFFSET                  ((unsigned long)(&((struct sector_hdr_data *)0)->end_info[0].time))
+#define SECTOR_END0_IDX_OFFSET                   ((unsigned long)(&((struct sector_hdr_data *)0)->end_info[0].index))
+#define SECTOR_END0_STATUS_OFFSET                ((unsigned long)(&((struct sector_hdr_data *)0)->end_info[0].status))
+#define SECTOR_END1_TIME_OFFSET                  ((unsigned long)(&((struct sector_hdr_data *)0)->end_info[1].time))
+#define SECTOR_END1_IDX_OFFSET                   ((unsigned long)(&((struct sector_hdr_data *)0)->end_info[1].index))
+#define SECTOR_END1_STATUS_OFFSET                ((unsigned long)(&((struct sector_hdr_data *)0)->end_info[1].status))
+
+/* the next address is get failed */
+#define FAILED_ADDR                              0xFFFFFFFF
+
+#define db_name(db)                              (((fdb_db_t)db)->name)
+#define db_init_ok(db)                           (((fdb_db_t)db)->init_ok)
+#define db_sec_size(db)                          (((fdb_db_t)db)->sec_size)
+#define db_max_size(db)                          (((fdb_db_t)db)->max_size)
+#define db_oldest_addr(db)                       (((fdb_db_t)db)->oldest_addr)
+
+#define db_lock(db)                                                            \
+    do {                                                                       \
+        if (((fdb_db_t)db)->lock) ((fdb_db_t)db)->lock((fdb_db_t)db);          \
+    } while(0);
+
+#define db_unlock(db)                                                          \
+    do {                                                                       \
+        if (((fdb_db_t)db)->unlock) ((fdb_db_t)db)->unlock((fdb_db_t)db);      \
+    } while(0);
+
+#define _FDB_WRITE_STATUS(db, addr, status_table, status_num, status_index, sync)    \
+    do {                                                                       \
+        result = _fdb_write_status((fdb_db_t)db, addr, status_table, status_num, status_index, sync);\
+        if (result != FDB_NO_ERR) return result;                               \
+    } while(0);
+
+#define FLASH_WRITE(db, addr, buf, size, sync)                                 \
+    do {                                                                       \
+        result = _fdb_flash_write((fdb_db_t)db, addr, buf, size, sync);        \
+        if (result != FDB_NO_ERR) return result;                               \
+    } while(0);
+
+struct sector_hdr_data {
+    uint8_t status[FDB_STORE_STATUS_TABLE_SIZE]; /**< sector store status @see fdb_sector_store_status_t */
+    uint32_t magic;                              /**< magic word(`T`, `S`, `L`, `0`) */
+    fdb_time_t start_time;                       /**< the first start node's timestamp */
+    struct {
+        fdb_time_t time;                         /**< the last end node's timestamp */
+        uint32_t index;                          /**< the last end node's index */
+        uint8_t status[TSL_STATUS_TABLE_SIZE];   /**< end node status, @see fdb_tsl_status_t */
+    } end_info[2];
+    uint32_t reserved;
+};
+typedef struct sector_hdr_data *sector_hdr_data_t;
+
+/* time series log node index data */
+struct log_idx_data {
+    uint8_t status_table[TSL_STATUS_TABLE_SIZE]; /**< node status, @see fdb_tsl_status_t */
+    fdb_time_t time;                             /**< node timestamp */
+    uint32_t log_len;                            /**< node total length (header + name + value), must align by FDB_WRITE_GRAN */
+    uint32_t log_addr;                           /**< node address */
+};
+typedef struct log_idx_data *log_idx_data_t;
+
+struct query_count_args {
+    fdb_tsl_status_t status;
+    size_t count;
+};
+
+struct check_sec_hdr_cb_args {
+    fdb_tsdb_t db;
+    bool check_failed;
+    size_t empty_num;
+    uint32_t empty_addr;
+};
+
+static fdb_err_t read_tsl(fdb_tsdb_t db, fdb_tsl_t tsl)
+{
+    struct log_idx_data idx;
+    /* read TSL index raw data */
+    _fdb_flash_read((fdb_db_t)db, tsl->addr.index, (uint32_t *) &idx, sizeof(struct log_idx_data));
+    tsl->status = (fdb_tsl_status_t) _fdb_get_status(idx.status_table, FDB_TSL_STATUS_NUM);
+    if ((tsl->status == FDB_TSL_PRE_WRITE) || (tsl->status == FDB_TSL_UNUSED)) {
+        tsl->log_len = db->max_len;
+        tsl->addr.log = FDB_DATA_UNUSED;
+        tsl->time = 0;
+    } else {
+        tsl->log_len = idx.log_len;
+        tsl->addr.log = idx.log_addr;
+        tsl->time = idx.time;
+    }
+
+    return FDB_NO_ERR;
+}
+
+static uint32_t get_next_sector_addr(fdb_tsdb_t db, tsdb_sec_info_t pre_sec, uint32_t traversed_len)
+{
+    if (traversed_len + db_sec_size(db) <= db_max_size(db)) {
+        if (pre_sec->addr + db_sec_size(db) < db_max_size(db)) {
+            return pre_sec->addr + db_sec_size(db);
+        } else {
+            /* the next sector is on the top of the database */
+            return 0;
+        }
+    } else {
+        /* finished */
+        return FAILED_ADDR;
+    }
+}
+
+static uint32_t get_next_tsl_addr(tsdb_sec_info_t sector, fdb_tsl_t pre_tsl)
+{
+    uint32_t addr = FAILED_ADDR;
+
+    if (sector->status == FDB_SECTOR_STORE_EMPTY) {
+        return FAILED_ADDR;
+    }
+
+    if (pre_tsl->addr.index + LOG_IDX_DATA_SIZE <= sector->end_idx) {
+        addr = pre_tsl->addr.index + LOG_IDX_DATA_SIZE;
+    } else {
+        /* no TSL */
+        return FAILED_ADDR;
+    }
+
+    return addr;
+}
+
+static uint32_t get_last_tsl_addr(tsdb_sec_info_t sector, fdb_tsl_t pre_tsl)
+{
+    uint32_t addr = FAILED_ADDR;
+
+    if (sector->status == FDB_SECTOR_STORE_EMPTY) {
+        return FAILED_ADDR;
+    }
+
+    if (pre_tsl->addr.index >= (sector->addr + SECTOR_HDR_DATA_SIZE + LOG_IDX_DATA_SIZE)) {
+        addr = pre_tsl->addr.index - LOG_IDX_DATA_SIZE;
+    } else {
+        return FAILED_ADDR;
+    }
+
+    return addr;
+}
+
+static uint32_t get_last_sector_addr(fdb_tsdb_t db, tsdb_sec_info_t pre_sec, uint32_t traversed_len)
+{
+    if (traversed_len + db_sec_size(db) <= db_max_size(db)) {
+        if (pre_sec->addr >= db_sec_size(db)) {
+            /* the next sector is previous sector */
+            return pre_sec->addr - db_sec_size(db);
+        } else {
+            /* the next sector is the last sector */
+            return db_max_size(db) - db_sec_size(db);
+        }
+    } else {
+        return FAILED_ADDR;
+    }
+}
+
+static fdb_err_t read_sector_info(fdb_tsdb_t db, uint32_t addr, tsdb_sec_info_t sector, bool traversal)
+{
+    fdb_err_t result = FDB_NO_ERR;
+    struct sector_hdr_data sec_hdr;
+
+    FDB_ASSERT(sector);
+
+    /* read sector header raw data */
+    _fdb_flash_read((fdb_db_t)db, addr, (uint32_t *)&sec_hdr, sizeof(struct sector_hdr_data));
+
+    sector->addr = addr;
+    sector->magic = sec_hdr.magic;
+
+    /* check magic word */
+    if (sector->magic != SECTOR_MAGIC_WORD) {
+        sector->check_ok = false;
+        return FDB_INIT_FAILED;
+    }
+    sector->check_ok = true;
+    sector->status = (fdb_sector_store_status_t) _fdb_get_status(sec_hdr.status, FDB_SECTOR_STORE_STATUS_NUM);
+    sector->start_time = sec_hdr.start_time;
+    sector->end_info_stat[0] = (fdb_tsl_status_t) _fdb_get_status(sec_hdr.end_info[0].status, FDB_TSL_STATUS_NUM);
+    sector->end_info_stat[1] = (fdb_tsl_status_t) _fdb_get_status(sec_hdr.end_info[1].status, FDB_TSL_STATUS_NUM);
+    if (sector->end_info_stat[0] == FDB_TSL_WRITE) {
+        sector->end_time = sec_hdr.end_info[0].time;
+        sector->end_idx = sec_hdr.end_info[0].index;
+    } else if (sector->end_info_stat[1] == FDB_TSL_WRITE) {
+        sector->end_time = sec_hdr.end_info[1].time;
+        sector->end_idx = sec_hdr.end_info[1].index;
+    } else if (sector->end_info_stat[0] == FDB_TSL_PRE_WRITE && sector->end_info_stat[1] == FDB_TSL_PRE_WRITE) {
+        //TODO There is no valid end node info on this sector, need impl fast query this sector by fdb_tsl_iter_by_time
+        FDB_ASSERT(0);
+    }
+    /* traversal all TSL and calculate the remain space size */
+    sector->empty_idx = sector->addr + SECTOR_HDR_DATA_SIZE;
+    sector->empty_data = sector->addr + db_sec_size(db);
+    /* the TSL's data is saved from sector bottom, and the TSL's index saved from the sector top */
+    sector->remain = sector->empty_data - sector->empty_idx;
+    if (sector->status == FDB_SECTOR_STORE_USING && traversal) {
+        struct fdb_tsl tsl;
+
+        tsl.addr.index = sector->empty_idx;
+        while (read_tsl(db, &tsl) == FDB_NO_ERR) {
+            if (tsl.status == FDB_TSL_UNUSED) {
+                break;
+            }
+            sector->end_time = tsl.time;
+            sector->end_idx = tsl.addr.index;
+            sector->empty_idx += LOG_IDX_DATA_SIZE;
+            sector->empty_data -= FDB_WG_ALIGN(tsl.log_len);
+            tsl.addr.index += LOG_IDX_DATA_SIZE;
+            if (sector->remain > LOG_IDX_DATA_SIZE + FDB_WG_ALIGN(tsl.log_len)) {
+                sector->remain -= (LOG_IDX_DATA_SIZE + FDB_WG_ALIGN(tsl.log_len));
+            } else {
+                FDB_INFO("Error: this TSL (0x%08" PRIX32 ") size (%" PRIu32 ") is out of bound.\n", tsl.addr.index, tsl.log_len);
+                sector->remain = 0;
+                result = FDB_READ_ERR;
+                break;
+            }
+        }
+    }
+
+    return result;
+}
+
+static fdb_err_t format_sector(fdb_tsdb_t db, uint32_t addr)
+{
+    fdb_err_t result = FDB_NO_ERR;
+    struct sector_hdr_data sec_hdr;
+
+    FDB_ASSERT(addr % db_sec_size(db) == 0);
+
+    result = _fdb_flash_erase((fdb_db_t)db, addr, db_sec_size(db));
+    if (result == FDB_NO_ERR) {
+        _FDB_WRITE_STATUS(db, addr, sec_hdr.status, FDB_SECTOR_STORE_STATUS_NUM, FDB_SECTOR_STORE_EMPTY, true);
+        /* set the magic */
+        sec_hdr.magic = SECTOR_MAGIC_WORD;
+        FLASH_WRITE(db, addr + SECTOR_MAGIC_OFFSET, &sec_hdr.magic, sizeof(sec_hdr.magic), true);
+    }
+
+    return result;
+}
+
+static void sector_iterator(fdb_tsdb_t db, tsdb_sec_info_t sector, fdb_sector_store_status_t status, void *arg1,
+        void *arg2, bool (*callback)(tsdb_sec_info_t sector, void *arg1, void *arg2), bool traversal)
+{
+    uint32_t sec_addr = sector->addr, traversed_len = 0;
+
+    /* search all sectors */
+    do {
+        read_sector_info(db, sec_addr, sector, false);
+        if (status == FDB_SECTOR_STORE_UNUSED || status == sector->status) {
+            if (traversal) {
+                read_sector_info(db, sec_addr, sector, true);
+            }
+            /* iterator is interrupted when callback return true */
+            if (callback && callback(sector, arg1, arg2)) {
+                return;
+            }
+        }
+        traversed_len += db_sec_size(db);
+    } while ((sec_addr = get_next_sector_addr(db, sector, traversed_len)) != FAILED_ADDR);
+}
+
+static fdb_err_t write_tsl(fdb_tsdb_t db, fdb_blob_t blob, fdb_time_t time)
+{
+    fdb_err_t result = FDB_NO_ERR;
+    struct log_idx_data idx;
+    uint32_t idx_addr = db->cur_sec.empty_idx;
+
+    idx.log_len = blob->size;
+    idx.time = time;
+    idx.log_addr = db->cur_sec.empty_data - FDB_WG_ALIGN(idx.log_len);
+    /* write the status will by write granularity */
+    _FDB_WRITE_STATUS(db, idx_addr, idx.status_table, FDB_TSL_STATUS_NUM, FDB_TSL_PRE_WRITE, false);
+    /* write other index info */
+    FLASH_WRITE(db, idx_addr + LOG_IDX_TS_OFFSET, &idx.time,  sizeof(struct log_idx_data) - LOG_IDX_TS_OFFSET, false);
+    /* write blob data */
+    FLASH_WRITE(db, idx.log_addr, blob->buf, blob->size, false);
+    /* write the status will by write granularity */
+    _FDB_WRITE_STATUS(db, idx_addr, idx.status_table, FDB_TSL_STATUS_NUM, FDB_TSL_WRITE, true);
+
+    return result;
+}
+
+static fdb_err_t update_sec_status(fdb_tsdb_t db, tsdb_sec_info_t sector, fdb_blob_t blob, fdb_time_t cur_time)
+{
+    fdb_err_t result = FDB_NO_ERR;
+    uint8_t status[FDB_STORE_STATUS_TABLE_SIZE];
+
+    if (sector->status == FDB_SECTOR_STORE_USING && sector->remain < LOG_IDX_DATA_SIZE + FDB_WG_ALIGN(blob->size)) {
+        uint8_t end_status[TSL_STATUS_TABLE_SIZE];
+        uint32_t end_index = sector->empty_idx - LOG_IDX_DATA_SIZE, new_sec_addr, cur_sec_addr = sector->addr;
+        /* save the end node index and timestamp */
+        if (sector->end_info_stat[0] == FDB_TSL_UNUSED) {
+            _FDB_WRITE_STATUS(db, cur_sec_addr + SECTOR_END0_STATUS_OFFSET, end_status, FDB_TSL_STATUS_NUM, FDB_TSL_PRE_WRITE, false);
+            FLASH_WRITE(db, cur_sec_addr + SECTOR_END0_TIME_OFFSET, (uint32_t * )&db->last_time, sizeof(fdb_time_t), false);
+            FLASH_WRITE(db, cur_sec_addr + SECTOR_END0_IDX_OFFSET, &end_index, sizeof(end_index), false);
+            _FDB_WRITE_STATUS(db, cur_sec_addr + SECTOR_END0_STATUS_OFFSET, end_status, FDB_TSL_STATUS_NUM, FDB_TSL_WRITE, true);
+        } else if (sector->end_info_stat[1] == FDB_TSL_UNUSED) {
+            _FDB_WRITE_STATUS(db, cur_sec_addr + SECTOR_END1_STATUS_OFFSET, end_status, FDB_TSL_STATUS_NUM, FDB_TSL_PRE_WRITE, false);
+            FLASH_WRITE(db, cur_sec_addr + SECTOR_END1_TIME_OFFSET, (uint32_t * )&db->last_time, sizeof(fdb_time_t), false);
+            FLASH_WRITE(db, cur_sec_addr + SECTOR_END1_IDX_OFFSET, &end_index, sizeof(end_index), false);
+            _FDB_WRITE_STATUS(db, cur_sec_addr + SECTOR_END1_STATUS_OFFSET, end_status, FDB_TSL_STATUS_NUM, FDB_TSL_WRITE, true);
+        }
+        /* change current sector to full */
+        _FDB_WRITE_STATUS(db, cur_sec_addr, status, FDB_SECTOR_STORE_STATUS_NUM, FDB_SECTOR_STORE_FULL, true);
+        sector->status = FDB_SECTOR_STORE_FULL;
+        /* calculate next sector address */
+        if (sector->addr + db_sec_size(db) < db_max_size(db)) {
+            new_sec_addr = sector->addr + db_sec_size(db);
+        }
+        else if (db->rollover) {
+            new_sec_addr = 0;
+        } else {
+            /* not rollover */
+            return FDB_SAVED_FULL;
+        }
+        read_sector_info(db, new_sec_addr, &db->cur_sec, false);
+        if (sector->status != FDB_SECTOR_STORE_EMPTY) {
+            /* calculate the oldest sector address */
+            if (new_sec_addr + db_sec_size(db) < db_max_size(db)) {
+                db_oldest_addr(db) = new_sec_addr + db_sec_size(db);
+            } else {
+                db_oldest_addr(db) = 0;
+            }
+            format_sector(db, new_sec_addr);
+            read_sector_info(db, new_sec_addr, &db->cur_sec, false);
+        }
+    } else if (sector->status == FDB_SECTOR_STORE_FULL) {
+        /* database full */
+        return FDB_SAVED_FULL;
+    }
+
+    if (sector->status == FDB_SECTOR_STORE_EMPTY) {
+        /* change the sector to using */
+        sector->status = FDB_SECTOR_STORE_USING;
+        sector->start_time = cur_time;
+        _FDB_WRITE_STATUS(db, sector->addr, status, FDB_SECTOR_STORE_STATUS_NUM, FDB_SECTOR_STORE_USING, true);
+        /* save the start timestamp */
+        FLASH_WRITE(db, sector->addr + SECTOR_START_TIME_OFFSET, (uint32_t *)&cur_time, sizeof(fdb_time_t), true);
+    }
+
+    return result;
+}
+
+static fdb_err_t tsl_append(fdb_tsdb_t db, fdb_blob_t blob)
+{
+    fdb_err_t result = FDB_NO_ERR;
+    fdb_time_t cur_time = db->get_time();
+
+    FDB_ASSERT(blob->size <= db->max_len);
+
+    /* check the current timestamp, MUST more than the last save timestamp */
+    if (cur_time <= db->last_time) {
+        FDB_INFO("Warning: current timestamp (%" PRIdMAX ") is less than or equal to the last save timestamp (%" PRIdMAX "). This tsl will be dropped.\n",
+                (intmax_t )cur_time, (intmax_t )(db->last_time));
+        return FDB_WRITE_ERR;
+    }
+
+    result = update_sec_status(db, &db->cur_sec, blob, cur_time);
+    if (result != FDB_NO_ERR) {
+        return result;
+    }
+
+    /* write the TSL node */
+    result = write_tsl(db, blob, cur_time);
+    if (result != FDB_NO_ERR) {
+        return result;
+    }
+
+    /* recalculate the current using sector info */
+    db->cur_sec.end_idx = db->cur_sec.empty_idx;
+    db->cur_sec.end_time = cur_time;
+    db->cur_sec.empty_idx += LOG_IDX_DATA_SIZE;
+    db->cur_sec.empty_data -= FDB_WG_ALIGN(blob->size);
+    db->cur_sec.remain -= LOG_IDX_DATA_SIZE + FDB_WG_ALIGN(blob->size);
+    db->last_time = cur_time;
+
+    return result;
+}
+
+/**
+ * Append a new log to TSDB.
+ *
+ * @param db database object
+ * @param blob log blob data
+ *
+ * @return result
+ */
+fdb_err_t fdb_tsl_append(fdb_tsdb_t db, fdb_blob_t blob)
+{
+    fdb_err_t result = FDB_NO_ERR;
+
+    if (!db_init_ok(db)) {
+        FDB_INFO("Error: TSL (%s) isn't initialize OK.\n", db_name(db));
+        return FDB_INIT_FAILED;
+    }
+
+    db_lock(db);
+    result = tsl_append(db, blob);
+    db_unlock(db);
+
+    return result;
+}
+
+/**
+ * The TSDB iterator for each TSL.
+ *
+ * @param db database object
+ * @param cb callback
+ * @param arg callback argument
+ */
+void fdb_tsl_iter(fdb_tsdb_t db, fdb_tsl_cb cb, void *arg)
+{
+    struct tsdb_sec_info sector;
+    uint32_t sec_addr, traversed_len = 0;
+    struct fdb_tsl tsl;
+
+    if (!db_init_ok(db)) {
+        FDB_INFO("Error: TSL (%s) isn't initialize OK.\n", db_name(db));
+    }
+
+    if (cb == NULL) {
+        return;
+    }
+
+    sec_addr = db_oldest_addr(db);
+	db_lock(db);
+    /* search all sectors */
+    do {
+        traversed_len += db_sec_size(db);
+        if (read_sector_info(db, sec_addr, &sector, false) != FDB_NO_ERR) {
+            continue;
+        }
+        /* sector has TSL */
+        if (sector.status == FDB_SECTOR_STORE_USING || sector.status == FDB_SECTOR_STORE_FULL) {
+            if (sector.status == FDB_SECTOR_STORE_USING) {
+                /* copy the current using sector status  */
+                sector = db->cur_sec;
+            }
+            tsl.addr.index = sector.addr + SECTOR_HDR_DATA_SIZE;
+            /* search all TSL */
+            do {
+                read_tsl(db, &tsl);
+                /* iterator is interrupted when callback return true */
+                if (cb(&tsl, arg)) {
+                    db_unlock(db);
+                    return;
+                }
+            } while ((tsl.addr.index = get_next_tsl_addr(&sector, &tsl)) != FAILED_ADDR);
+        }
+    } while ((sec_addr = get_next_sector_addr(db, &sector, traversed_len)) != FAILED_ADDR);
+    db_unlock(db);
+}
+
+/**
+ * The TSDB iterator for each TSL.
+ *
+ * @param db database object
+ * @param cb callback
+ * @param arg callback argument
+ */
+void fdb_tsl_iter_reverse(fdb_tsdb_t db, fdb_tsl_cb cb, void *cb_arg)
+{
+    struct tsdb_sec_info sector;
+    uint32_t sec_addr, traversed_len = 0;
+    struct fdb_tsl tsl;
+
+    if (!db_init_ok(db)) {
+        FDB_INFO("Error: TSL (%s) isn't initialize OK.\n", db_name(db));
+    }
+
+    if (cb == NULL) {
+        return;
+    }
+
+    sec_addr = db->cur_sec.addr;
+    db_lock(db);
+    /* search all sectors */
+    do {
+        traversed_len += db_sec_size(db);
+        if (read_sector_info(db, sec_addr, &sector, false) != FDB_NO_ERR) {
+            continue;
+        }
+        /* sector has TSL */
+        if (sector.status == FDB_SECTOR_STORE_USING || sector.status == FDB_SECTOR_STORE_FULL) {
+            if (sector.status == FDB_SECTOR_STORE_USING) {
+                /* copy the current using sector status  */
+                sector = db->cur_sec;
+            }
+            tsl.addr.index = sector.end_idx;
+            /* search all TSL */
+            do {
+                read_tsl(db, &tsl);
+                /* iterator is interrupted when callback return true */
+                if (cb(&tsl, cb_arg)) {
+                    goto __exit;
+                }
+            } while ((tsl.addr.index = get_last_tsl_addr(&sector, &tsl)) != FAILED_ADDR);
+        } else if (sector.status == FDB_SECTOR_STORE_EMPTY || sector.status == FDB_SECTOR_STORE_UNUSED)
+            goto __exit;
+    } while ((sec_addr = get_last_sector_addr(db, &sector, traversed_len)) != FAILED_ADDR);
+
+__exit:
+    db_unlock(db);
+}
+
+/*
+ * Found the matched TSL address.
+ */
+static int search_start_tsl_addr(fdb_tsdb_t db, int start, int end, fdb_time_t from, fdb_time_t to)
+{
+    struct fdb_tsl tsl;
+    while (true) {
+        tsl.addr.index = start + FDB_ALIGN((end - start) / 2, LOG_IDX_DATA_SIZE);
+        read_tsl(db, &tsl);
+        if (tsl.time < from) {
+            start = tsl.addr.index + LOG_IDX_DATA_SIZE;
+        } else if (tsl.time > from) {
+            end = tsl.addr.index - LOG_IDX_DATA_SIZE;
+        } else {
+            return tsl.addr.index;
+        }
+
+        if (start > end) {
+            if (from > to) {
+                tsl.addr.index = start;
+                read_tsl(db, &tsl);
+                if (tsl.time > from) {
+                    start -= LOG_IDX_DATA_SIZE;
+                }
+            }
+            break;
+        }
+    }
+    return start;
+}
+
+/**
+ * The TSDB iterator for each TSL by timestamp.
+ *
+ * @param db database object
+ * @param from starting timestamp. It will be a reverse iterator when ending timestamp less than starting timestamp
+ * @param to ending timestamp
+ * @param cb callback
+ * @param arg callback argument
+ */
+void fdb_tsl_iter_by_time(fdb_tsdb_t db, fdb_time_t from, fdb_time_t to, fdb_tsl_cb cb, void *cb_arg)
+{
+    struct tsdb_sec_info sector;
+    uint32_t sec_addr, start_addr, traversed_len = 0;
+    struct fdb_tsl tsl;
+    bool found_start_tsl = false;
+
+    uint32_t (*get_sector_addr)(fdb_tsdb_t , tsdb_sec_info_t , uint32_t);
+    uint32_t (*get_tsl_addr)(tsdb_sec_info_t , fdb_tsl_t);
+
+    if (!db_init_ok(db)) {
+        FDB_INFO("Error: TSL (%s) isn't initialize OK.\n", db_name(db));
+    }
+
+    if(from <= to) {
+        start_addr = db_oldest_addr(db);
+        get_sector_addr = get_next_sector_addr;
+        get_tsl_addr = get_next_tsl_addr;
+    } else {
+        start_addr = db->cur_sec.addr;
+        get_sector_addr = get_last_sector_addr;
+        get_tsl_addr = get_last_tsl_addr;
+    }
+
+//    FDB_INFO("from %s", ctime((const time_t * )&from));
+//    FDB_INFO("to %s", ctime((const time_t * )&to));
+
+    if (cb == NULL) {
+        return;
+    }
+
+    sec_addr = start_addr;
+    db_lock(db);
+    /* search all sectors */
+    do {
+        traversed_len += db_sec_size(db);
+        if (read_sector_info(db, sec_addr, &sector, false) != FDB_NO_ERR) {
+            continue;
+        }
+        /* sector has TSL */
+        if ((sector.status == FDB_SECTOR_STORE_USING || sector.status == FDB_SECTOR_STORE_FULL)) {
+            if (sector.status == FDB_SECTOR_STORE_USING) {
+                /* copy the current using sector status  */
+                sector = db->cur_sec;
+            }
+            if ((found_start_tsl)
+                    || (!found_start_tsl &&
+                            ((from <= to && ((sec_addr == start_addr && from <= sector.start_time) || from <= sector.end_time)) ||
+                             (from > to  && ((sec_addr == start_addr && from >= sector.end_time) || from >= sector.start_time)))
+                             )) {
+                uint32_t start = sector.addr + SECTOR_HDR_DATA_SIZE, end = sector.end_idx;
+
+                found_start_tsl = true;
+                /* search the first start TSL address */
+                tsl.addr.index = search_start_tsl_addr(db, start, end, from, to);
+                /* search all TSL */
+                do {
+                    read_tsl(db, &tsl);
+                    if (tsl.status != FDB_TSL_UNUSED) {
+                        if ((from <= to && tsl.time >= from && tsl.time <= to)
+                                || (from > to && tsl.time <= from && tsl.time >= to)) {
+                            /* iterator is interrupted when callback return true */
+                            if (cb(&tsl, cb_arg)) {
+                                goto __exit;
+                            }
+                        } else {
+                            goto __exit;
+                        }
+                    }
+                } while ((tsl.addr.index = get_tsl_addr(&sector, &tsl)) != FAILED_ADDR);
+            }
+        } else if (sector.status == FDB_SECTOR_STORE_EMPTY) {
+            goto __exit;
+        }
+    } while ((sec_addr = get_sector_addr(db, &sector, traversed_len)) != FAILED_ADDR);
+
+__exit:
+    db_unlock(db);
+}
+
+static bool query_count_cb(fdb_tsl_t tsl, void *arg)
+{
+    struct query_count_args *args = arg;
+
+    if (tsl->status == args->status) {
+        args->count++;
+    }
+
+    return false;
+}
+
+/**
+ * Query some TSL's count by timestamp and status.
+ *
+ * @param db database object
+ * @param from starting timestamp
+ * @param to ending timestamp
+ * @param status status
+ */
+size_t fdb_tsl_query_count(fdb_tsdb_t db, fdb_time_t from, fdb_time_t to, fdb_tsl_status_t status)
+{
+    struct query_count_args arg = { FDB_TSL_UNUSED, 0 };
+
+    arg.status = status;
+
+    if (!db_init_ok(db)) {
+        FDB_INFO("Error: TSL (%s) isn't initialize OK.\n", db_name(db));
+        return FDB_INIT_FAILED;
+    }
+
+    fdb_tsl_iter_by_time(db, from, to, query_count_cb, &arg);
+
+    return arg.count;
+
+}
+
+/**
+ * Set the TSL status.
+ *
+ * @param db database object
+ * @param tsl TSL object
+ * @param status status
+ *
+ * @return result
+ */
+fdb_err_t fdb_tsl_set_status(fdb_tsdb_t db, fdb_tsl_t tsl, fdb_tsl_status_t status)
+{
+    fdb_err_t result = FDB_NO_ERR;
+    uint8_t status_table[TSL_STATUS_TABLE_SIZE];
+
+    /* write the status will by write granularity */
+    _FDB_WRITE_STATUS(db, tsl->addr.index, status_table, FDB_TSL_STATUS_NUM, status, true);
+
+    return result;
+}
+
+/**
+ * Convert the TSL object to blob object
+ *
+ * @param tsl TSL object
+ * @param blob blob object
+ *
+ * @return new blob object
+ */
+fdb_blob_t fdb_tsl_to_blob(fdb_tsl_t tsl, fdb_blob_t blob)
+{
+    blob->saved.addr = tsl->addr.log;
+    blob->saved.meta_addr = tsl->addr.index;
+    blob->saved.len = tsl->log_len;
+
+    return blob;
+}
+
+static bool check_sec_hdr_cb(tsdb_sec_info_t sector, void *arg1, void *arg2)
+{
+    struct check_sec_hdr_cb_args *arg = arg1;
+    fdb_tsdb_t db = arg->db;
+
+    if (!sector->check_ok) {
+        FDB_INFO("Sector (0x%08" PRIX32 ") header info is incorrect.\n", sector->addr);
+        (arg->check_failed) = true;
+        return true;
+    } else if (sector->status == FDB_SECTOR_STORE_USING) {
+        if (db->cur_sec.addr == FDB_DATA_UNUSED) {
+            memcpy(&db->cur_sec, sector, sizeof(struct tsdb_sec_info));
+        } else {
+            FDB_INFO("Warning: Sector status is wrong, there are multiple sectors in use.\n");
+            (arg->check_failed) = true;
+            return true;
+        }
+    } else if (sector->status == FDB_SECTOR_STORE_EMPTY) {
+        (arg->empty_num) += 1;
+        arg->empty_addr = sector->addr;
+        if ((arg->empty_num) == 1 && db->cur_sec.addr == FDB_DATA_UNUSED) {
+            memcpy(&db->cur_sec, sector, sizeof(struct tsdb_sec_info));
+        }
+    }
+
+    return false;
+}
+static bool format_all_cb(tsdb_sec_info_t sector, void *arg1, void *arg2)
+{
+    fdb_tsdb_t db = arg1;
+
+    format_sector(db, sector->addr);
+
+    return false;
+}
+
+static void tsl_format_all(fdb_tsdb_t db)
+{
+    struct tsdb_sec_info sector;
+
+    sector.addr = 0;
+    sector_iterator(db, &sector, FDB_SECTOR_STORE_UNUSED, db, NULL, format_all_cb, false);
+    db_oldest_addr(db) = 0;
+    db->cur_sec.addr = 0;
+    db->last_time = 0;
+    /* read the current using sector info */
+    read_sector_info(db, db->cur_sec.addr, &db->cur_sec, false);
+
+    FDB_INFO("All sector format finished.\n");
+}
+
+/**
+ * Clean all the data in the TSDB.
+ *
+ * @note It's DANGEROUS. This operation is not reversible.
+ *
+ * @param db database object
+ */
+void fdb_tsl_clean(fdb_tsdb_t db)
+{
+    db_lock(db);
+    tsl_format_all(db);
+    db_unlock(db);
+}
+
+/**
+ * This function will get or set some options of the database
+ *
+ * @param db database object
+ * @param cmd the control command
+ * @param arg the argument
+ */
+void fdb_tsdb_control(fdb_tsdb_t db, int cmd, void *arg)
+{
+    FDB_ASSERT(db);
+
+    switch (cmd) {
+    case FDB_TSDB_CTRL_SET_SEC_SIZE:
+        /* this change MUST before database initialization */
+        FDB_ASSERT(db->parent.init_ok == false);
+        db->parent.sec_size = *(uint32_t *)arg;
+        break;
+    case FDB_TSDB_CTRL_GET_SEC_SIZE:
+        *(uint32_t *)arg = db->parent.sec_size;
+        break;
+    case FDB_TSDB_CTRL_SET_LOCK:
+#if !defined(__ARMCC_VERSION) && defined(__GNUC__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wpedantic"
+#endif
+        db->parent.lock = (void (*)(fdb_db_t db))arg;
+#if !defined(__ARMCC_VERSION) && defined(__GNUC__)
+#pragma GCC diagnostic pop
+#endif
+        break;
+    case FDB_TSDB_CTRL_SET_UNLOCK:
+#if !defined(__ARMCC_VERSION) && defined(__GNUC__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wpedantic"
+#endif
+        db->parent.unlock = (void (*)(fdb_db_t db))arg;
+#if !defined(__ARMCC_VERSION) && defined(__GNUC__)
+#pragma GCC diagnostic pop
+#endif
+        break;
+    case FDB_TSDB_CTRL_SET_ROLLOVER:
+        /* this change MUST after database initialized */
+        FDB_ASSERT(db->parent.init_ok == true);
+        db->rollover = *(bool *)arg;
+        break;
+    case FDB_TSDB_CTRL_GET_ROLLOVER:
+        *(bool *)arg = db->rollover;
+        break;
+    case FDB_TSDB_CTRL_GET_LAST_TIME:
+        *(fdb_time_t *)arg = db->last_time;
+        break;
+    case FDB_TSDB_CTRL_SET_FILE_MODE:
+#ifdef FDB_USING_FILE_MODE
+        /* this change MUST before database initialization */
+        FDB_ASSERT(db->parent.init_ok == false);
+        db->parent.file_mode = *(bool *)arg;
+#else
+        FDB_INFO("Error: set file mode Failed. Please defined the FDB_USING_FILE_MODE macro.");
+#endif
+        break;
+    case FDB_TSDB_CTRL_SET_MAX_SIZE:
+#ifdef FDB_USING_FILE_MODE
+        /* this change MUST before database initialization */
+        FDB_ASSERT(db->parent.init_ok == false);
+        db->parent.max_size = *(uint32_t *)arg;
+#endif
+        break;
+    case FDB_TSDB_CTRL_SET_NOT_FORMAT:
+        /* this change MUST before database initialization */
+        FDB_ASSERT(db->parent.init_ok == false);
+        db->parent.not_formatable = *(bool *)arg;
+        break;
+    }
+}
+
+/**
+ * The time series database initialization.
+ *
+ * @param db database object
+ * @param name database name
+ * @param path FAL mode: partition name, file mode: database saved directory path
+ * @param get_time get current time function
+ * @param max_len maximum length of each log
+ * @param user_data user data
+ *
+ * @return result
+ */
+fdb_err_t fdb_tsdb_init(fdb_tsdb_t db, const char *name, const char *path, fdb_get_time get_time, size_t max_len, void *user_data)
+{
+    fdb_err_t result = FDB_NO_ERR;
+    struct tsdb_sec_info sector;
+    struct check_sec_hdr_cb_args check_sec_arg = { db, false, 0, 0};
+
+    FDB_ASSERT(get_time);
+
+    result = _fdb_init_ex((fdb_db_t)db, name, path, FDB_DB_TYPE_TS, user_data);
+    if (result != FDB_NO_ERR) {
+        goto __exit;
+    }
+
+    db->get_time = get_time;
+    db->max_len = max_len;
+    /* default rollover flag is true */
+    db->rollover = true;
+    db_oldest_addr(db) = FDB_DATA_UNUSED;
+    db->cur_sec.addr = FDB_DATA_UNUSED;
+    /* must less than sector size */
+    FDB_ASSERT(max_len < db_sec_size(db));
+
+    /* check all sector header */
+    sector.addr = 0;
+    sector_iterator(db, &sector, FDB_SECTOR_STORE_UNUSED, &check_sec_arg, NULL, check_sec_hdr_cb, true);
+    /* format all sector when check failed */
+    if (check_sec_arg.check_failed) {
+        if (db->parent.not_formatable) {
+            result = FDB_READ_ERR;
+            goto __exit;
+        } else {
+            tsl_format_all(db);
+        }
+    } else {
+        uint32_t latest_addr;
+        if (check_sec_arg.empty_num > 0) {
+            latest_addr = check_sec_arg.empty_addr;
+        } else {
+            if (db->rollover) {
+                latest_addr = db->cur_sec.addr;
+            } else {
+                /* There is no empty sector. */
+                latest_addr = db->cur_sec.addr = db_max_size(db) - db_sec_size(db);
+            }
+        }
+        /* db->cur_sec is the latest sector, and the next is the oldest sector */
+        if (latest_addr + db_sec_size(db) >= db_max_size(db)) {
+            /* db->cur_sec is the the bottom of the database */
+            db_oldest_addr(db) = 0;
+        } else {
+            db_oldest_addr(db) = latest_addr + db_sec_size(db);
+        }
+    }
+    FDB_DEBUG("TSDB (%s) oldest sectors is 0x%08" PRIX32 ", current using sector is 0x%08" PRIX32 ".\n", db_name(db), db_oldest_addr(db),
+            db->cur_sec.addr);
+    /* read the current using sector info */
+    read_sector_info(db, db->cur_sec.addr, &db->cur_sec, true);
+    /* get last save time */
+    if (db->cur_sec.status == FDB_SECTOR_STORE_USING) {
+        db->last_time = db->cur_sec.end_time;
+    } else if (db->cur_sec.status == FDB_SECTOR_STORE_EMPTY && db_oldest_addr(db) != db->cur_sec.addr) {
+        struct tsdb_sec_info sec;
+        uint32_t addr = db->cur_sec.addr;
+
+        if (addr == 0) {
+            addr = db_max_size(db) - db_sec_size(db);
+        } else {
+            addr -= db_sec_size(db);
+        }
+        read_sector_info(db, addr, &sec, false);
+        db->last_time = sec.end_time;
+    }
+
+__exit:
+
+    _fdb_init_finish((fdb_db_t)db, result);
+
+    return result;
+}
+
+/**
+ * The time series database deinitialization.
+ *
+ * @param db database object
+ *
+ * @return result
+ */
+fdb_err_t fdb_tsdb_deinit(fdb_tsdb_t db)
+{
+    _fdb_deinit((fdb_db_t) db);
+
+    return FDB_NO_ERR;
+}
+
+#endif /* defined(FDB_USING_TSDB) */

+ 320 - 0
package/flashdb/fdb_utils.c

@@ -0,0 +1,320 @@
+/*
+ * Copyright (c) 2020, Armink, <armink.ztl@gmail.com>
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/**
+ * @file
+ * @brief utils
+ *
+ * Some utils for this library.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "flashdb.h"
+#include "fdb_low_lvl.h"
+
+#define FDB_LOG_TAG "[utils]"
+
+static const uint32_t crc32_table[] =
+{
+    0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
+    0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
+    0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
+    0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+    0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
+    0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
+    0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
+    0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+    0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
+    0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+    0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
+    0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+    0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
+    0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
+    0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
+    0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+    0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
+    0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
+    0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
+    0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+    0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
+    0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
+    0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
+    0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+    0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
+    0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
+    0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
+    0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+    0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
+    0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+    0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
+    0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+    0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
+    0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
+    0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
+    0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+    0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
+    0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
+    0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
+    0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+    0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
+    0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
+    0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
+};
+
+/**
+ * Calculate the CRC32 value of a memory buffer.
+ *
+ * @param crc accumulated CRC32 value, must be 0 on first call
+ * @param buf buffer to calculate CRC32 value for
+ * @param size bytes in buffer
+ *
+ * @return calculated CRC32 value
+ */
+uint32_t fdb_calc_crc32(uint32_t crc, const void *buf, size_t size)
+{
+    const uint8_t *p;
+
+    p = (const uint8_t *)buf;
+    crc = crc ^ ~0U;
+
+    while (size--) {
+        crc = crc32_table[(crc ^ *p++) & 0xFF] ^ (crc >> 8);
+    }
+
+    return crc ^ ~0U;
+}
+
+size_t _fdb_set_status(uint8_t status_table[], size_t status_num, size_t status_index)
+{
+    size_t byte_index = ~0UL;
+    /*
+     * | write garn |       status0       |       status1       |      status2         |       status3      |
+     * ------------------------------------------------------------------------------------------------------
+     * |    1bit    | 0xFF                | 0x7F                |  0x3F                |  0x1F
+     * ------------------------------------------------------------------------------------------------------
+     * |    8bit    | 0xFF FF FF          | 0x00 FF FF          |  0x00 00 FF          |  0x00 00 00
+     * ------------------------------------------------------------------------------------------------------
+     * |   32bit    | 0xFFFFFFFF FFFFFFFF | 0x00FFFFFF FFFFFFFF |  0x00FFFFFF 00FFFFFF |  0x00FFFFFF 00FFFFFF
+     * |            | 0xFFFFFFFF          | 0xFFFFFFFF          |  0xFFFFFFFF          |  0x00FFFFFF
+     * ------------------------------------------------------------------------------------------------------
+     * |            | 0xFFFFFFFF FFFFFFFF | 0x00FFFFFF FFFFFFFF |  0x00FFFFFF FFFFFFFF |  0x00FFFFFF FFFFFFFF
+     * |   64bit    | 0xFFFFFFFF FFFFFFFF | 0xFFFFFFFF FFFFFFFF |  0x00FFFFFF FFFFFFFF |  0x00FFFFFF FFFFFFFF
+     * |            | 0xFFFFFFFF FFFFFFFF | 0xFFFFFFFF FFFFFFFF |  0xFFFFFFFF FFFFFFFF |  0x00FFFFFF FFFFFFFF
+     */
+    memset(status_table, FDB_BYTE_ERASED, FDB_STATUS_TABLE_SIZE(status_num));
+    if (status_index > 0) {
+#if (FDB_WRITE_GRAN == 1)
+        byte_index = (status_index - 1) / 8;
+#if (FDB_BYTE_ERASED == 0xFF)
+        status_table[byte_index] &= (0x00ff >> (status_index % 8));
+#else
+        status_table[byte_index] |= (0x00ff >> (status_index % 8));
+#endif
+#else
+        byte_index = (status_index - 1) * (FDB_WRITE_GRAN / 8);
+        status_table[byte_index] = FDB_BYTE_WRITTEN;
+#endif /* FDB_WRITE_GRAN == 1 */
+    }
+
+    return byte_index;
+}
+
+size_t _fdb_get_status(uint8_t status_table[], size_t status_num)
+{
+    size_t i = 0, status_num_bak = --status_num;
+
+    while (status_num --) {
+        /* get the first 0 position from end address to start address */
+#if (FDB_WRITE_GRAN == 1)
+        if ((status_table[status_num / 8] & (0x80 >> (status_num % 8))) == 0x00) {
+            break;
+        }
+#else /*  (FDB_WRITE_GRAN == 8) ||  (FDB_WRITE_GRAN == 32) ||  (FDB_WRITE_GRAN == 64) */
+        if (status_table[status_num * FDB_WRITE_GRAN / 8] == FDB_BYTE_WRITTEN) {
+            break;
+        }
+#endif /* FDB_WRITE_GRAN == 1 */
+        i++;
+    }
+
+    return status_num_bak - i;
+}
+
+fdb_err_t _fdb_write_status(fdb_db_t db, uint32_t addr, uint8_t status_table[], size_t status_num, size_t status_index, bool sync)
+{
+    fdb_err_t result = FDB_NO_ERR;
+    size_t byte_index;
+
+    FDB_ASSERT(status_index < status_num);
+    FDB_ASSERT(status_table);
+
+    /* set the status first */
+    byte_index = _fdb_set_status(status_table, status_num, status_index);
+
+    /* the first status table value is all 1, so no need to write flash */
+    if (byte_index == ~0UL) {
+        return FDB_NO_ERR;
+    }
+#if (FDB_WRITE_GRAN == 1)
+    result = _fdb_flash_write(db, addr + byte_index, (uint32_t *)&status_table[byte_index], 1, sync);
+#else /*  (FDB_WRITE_GRAN == 8) ||  (FDB_WRITE_GRAN == 32) ||  (FDB_WRITE_GRAN == 64) */
+    /* write the status by write granularity
+     * some flash (like stm32 onchip) NOT supported repeated write before erase */
+    result = _fdb_flash_write(db, addr + byte_index, (uint32_t *) &status_table[byte_index], FDB_WRITE_GRAN / 8, sync);
+#endif /* FDB_WRITE_GRAN == 1 */
+
+    return result;
+}
+
+size_t _fdb_read_status(fdb_db_t db, uint32_t addr, uint8_t status_table[], size_t total_num)
+{
+    FDB_ASSERT(status_table);
+
+    _fdb_flash_read(db, addr, (uint32_t *) status_table, FDB_STATUS_TABLE_SIZE(total_num));
+
+    return _fdb_get_status(status_table, total_num);
+}
+
+/*
+ * find the continue 0xFF flash address to end address
+ */
+uint32_t _fdb_continue_ff_addr(fdb_db_t db, uint32_t start, uint32_t end)
+{
+    uint8_t buf[32], last_data = FDB_BYTE_WRITTEN;
+    size_t i, addr = start, read_size;
+
+    for (; start < end; start += sizeof(buf)) {
+        if (start + sizeof(buf) < end) {
+            read_size = sizeof(buf);
+        } else {
+            read_size = end - start;
+        }
+        _fdb_flash_read(db, start, (uint32_t *) buf, read_size);
+        for (i = 0; i < read_size; i++) {
+            if (last_data != FDB_BYTE_ERASED && buf[i] == FDB_BYTE_ERASED) {
+                addr = start + i;
+            }
+            last_data = buf[i];
+        }
+    }
+
+    if (last_data == FDB_BYTE_ERASED) {
+        return FDB_WG_ALIGN(addr);
+    } else {
+        return end;
+    }
+}
+
+/**
+ * Make a blob object.
+ *
+ * @param blob blob object
+ * @param value_buf value buffer
+ * @param buf_len buffer length
+ *
+ * @return new blob object
+ */
+fdb_blob_t fdb_blob_make(fdb_blob_t blob, const void *value_buf, size_t buf_len)
+{
+    blob->buf = (void *)value_buf;
+    blob->size = buf_len;
+
+    return blob;
+}
+
+/**
+ * Read the blob object in database.
+ *
+ * @param db database object
+ * @param blob blob object
+ *
+ * @return read length
+ */
+size_t fdb_blob_read(fdb_db_t db, fdb_blob_t blob)
+{
+    size_t read_len = blob->size;
+
+    if (read_len > blob->saved.len) {
+        read_len = blob->saved.len;
+    }
+    if (_fdb_flash_read(db, blob->saved.addr, blob->buf, read_len) != FDB_NO_ERR) {
+        read_len = 0;
+    }
+
+    return read_len;
+}
+
+#ifdef FDB_USING_FILE_MODE
+extern fdb_err_t _fdb_file_read(fdb_db_t db, uint32_t addr, void *buf, size_t size);
+extern fdb_err_t _fdb_file_write(fdb_db_t db, uint32_t addr, const void *buf, size_t size, bool sync);
+extern fdb_err_t _fdb_file_erase(fdb_db_t db, uint32_t addr, size_t size);
+#endif /* FDB_USING_FILE_LIBC */
+
+fdb_err_t _fdb_flash_read(fdb_db_t db, uint32_t addr, void *buf, size_t size)
+{
+    fdb_err_t result = FDB_NO_ERR;
+
+    if (db->file_mode) {
+#ifdef FDB_USING_FILE_MODE
+        return _fdb_file_read(db, addr, buf, size);
+#else
+        return FDB_READ_ERR;
+#endif
+    } else {
+#ifdef FDB_USING_FAL_MODE
+        if (fal_partition_read(db->storage.part, addr, (uint8_t *) buf, size) < 0) {
+            result = FDB_READ_ERR;
+        }
+#endif
+    }
+
+    return result;
+}
+
+fdb_err_t _fdb_flash_erase(fdb_db_t db, uint32_t addr, size_t size)
+{
+    fdb_err_t result = FDB_NO_ERR;
+
+    if (db->file_mode) {
+#ifdef FDB_USING_FILE_MODE
+        return _fdb_file_erase(db, addr, size);
+#else
+        return FDB_ERASE_ERR;
+#endif /* FDB_USING_FILE_MODE */
+    } else {
+#ifdef FDB_USING_FAL_MODE
+        if (fal_partition_erase(db->storage.part, addr, size) < 0) {
+            result = FDB_ERASE_ERR;
+        }
+#endif
+    }
+
+    return result;
+}
+
+fdb_err_t _fdb_flash_write(fdb_db_t db, uint32_t addr, const void *buf, size_t size, bool sync)
+{
+    fdb_err_t result = FDB_NO_ERR;
+
+    if (db->file_mode) {
+#ifdef FDB_USING_FILE_MODE
+        return _fdb_file_write(db, addr, buf, size, sync);
+#else
+        return FDB_READ_ERR;
+#endif /* FDB_USING_FILE_MODE */
+    } else {
+#ifdef FDB_USING_FAL_MODE
+        if (fal_partition_write(db->storage.part, addr, (uint8_t *)buf, size) < 0)
+        {
+            result = FDB_WRITE_ERR;
+        }
+#endif
+    }
+
+    return result;
+
+}

+ 77 - 0
package/flashdb/flashdb.h

@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2020, Armink, <armink.ztl@gmail.com>
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/**
+ * @file
+ * @brief Public APIs.
+ */
+
+#ifndef _FLASHDB_H_
+#define _FLASHDB_H_
+
+#include <stdint.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <time.h>
+#include "fdb_cfg.h"
+
+#ifdef FDB_USING_FAL_MODE
+#include "fal.h"
+#endif
+
+#include "fdb_def.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* FlashDB database API */
+fdb_err_t fdb_kvdb_init   (fdb_kvdb_t db, const char *name, const char *path, struct fdb_default_kv *default_kv,
+        void *user_data);
+void      fdb_kvdb_control(fdb_kvdb_t db, int cmd, void *arg);
+fdb_err_t fdb_kvdb_deinit(fdb_kvdb_t db);
+fdb_err_t fdb_tsdb_init   (fdb_tsdb_t db, const char *name, const char *path, fdb_get_time get_time, size_t max_len,
+        void *user_data);
+void      fdb_tsdb_control(fdb_tsdb_t db, int cmd, void *arg);
+fdb_err_t fdb_tsdb_deinit(fdb_tsdb_t db);
+
+/* blob API */
+fdb_blob_t fdb_blob_make     (fdb_blob_t blob, const void *value_buf, size_t buf_len);
+size_t     fdb_blob_read     (fdb_db_t db, fdb_blob_t blob);
+
+/* Key-Value API like a KV DB */
+fdb_err_t         fdb_kv_set          (fdb_kvdb_t db, const char *key, const char *value);
+char             *fdb_kv_get          (fdb_kvdb_t db, const char *key);
+fdb_err_t         fdb_kv_set_blob     (fdb_kvdb_t db, const char *key, fdb_blob_t blob);
+size_t            fdb_kv_get_blob     (fdb_kvdb_t db, const char *key, fdb_blob_t blob);
+fdb_err_t         fdb_kv_del          (fdb_kvdb_t db, const char *key);
+fdb_kv_t          fdb_kv_get_obj      (fdb_kvdb_t db, const char *key, fdb_kv_t kv);
+fdb_blob_t        fdb_kv_to_blob      (fdb_kv_t   kv, fdb_blob_t blob);
+fdb_err_t         fdb_kv_set_default  (fdb_kvdb_t db);
+void              fdb_kv_print        (fdb_kvdb_t db);
+fdb_kv_iterator_t fdb_kv_iterator_init(fdb_kvdb_t db, fdb_kv_iterator_t itr);
+bool              fdb_kv_iterate      (fdb_kvdb_t db, fdb_kv_iterator_t itr);
+
+/* Time series log API like a TSDB */
+fdb_err_t  fdb_tsl_append      (fdb_tsdb_t db, fdb_blob_t blob);
+void       fdb_tsl_iter        (fdb_tsdb_t db, fdb_tsl_cb cb, void *cb_arg);
+void       fdb_tsl_iter_reverse(fdb_tsdb_t db, fdb_tsl_cb cb, void *cb_arg);
+void       fdb_tsl_iter_by_time(fdb_tsdb_t db, fdb_time_t from, fdb_time_t to, fdb_tsl_cb cb, void *cb_arg);
+size_t     fdb_tsl_query_count (fdb_tsdb_t db, fdb_time_t from, fdb_time_t to, fdb_tsl_status_t status);
+fdb_err_t  fdb_tsl_set_status  (fdb_tsdb_t db, fdb_tsl_t tsl, fdb_tsl_status_t status);
+void       fdb_tsl_clean       (fdb_tsdb_t db);
+fdb_blob_t fdb_tsl_to_blob     (fdb_tsl_t tsl, fdb_blob_t blob);
+
+/* fdb_utils.c */
+uint32_t   fdb_calc_crc32(uint32_t crc, const void *buf, size_t size);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FLASHDB_H_ */

+ 29 - 0
package/flashdb/flashdb.py

@@ -0,0 +1,29 @@
+import _flashdb
+import struct
+
+
+class FDB(_flashdb.FlashDB):
+
+    def kv_get_blob(self, kvdb, key, size):
+        res = super().kv_get_blob(kvdb, key, size)
+        if type(res) == type([]):
+            return bytes(res)
+        return None
+
+    def kv_set_by_fmt(self, kvdb, key, v, fmt):
+        if type(v) == type([]) or type(v) == type(()):
+            blob = struct.pack(fmt, *v)
+            return super().kv_set_blob(kvdb, key, blob)
+        if type(v) == type(0):
+            blob = struct.pack(fmt, v)
+            return super().kv_set_blob(kvdb, key, blob)
+
+    def kv_get_by_fmt(self, kvdb, key, size, fmt):
+        res = super().kv_get_blob(kvdb, key, size)
+
+        if res is None:
+            return None
+        vs = struct.unpack(fmt, bytes(res))
+        if len(vs) == 1:
+            return vs[0]
+        return vs

+ 53 - 0
package/flashdb/kvdb_basic_sample.c

@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2020, Armink, <armink.ztl@gmail.com>
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/**
+ * @file
+ * @brief basic KV samples.
+ *
+ * basic Key-Value Database KV feature samples
+ * get and show currnet boot counts
+ */
+
+#include "flashdb.h"
+
+#ifdef FDB_USING_KVDB
+
+#define FDB_LOG_TAG "[sample][kvdb][basic]"
+
+void kvdb_basic_sample(fdb_kvdb_t kvdb)
+{
+    struct fdb_blob blob;
+    int boot_count = 0;
+
+    FDB_INFO("==================== kvdb_basic_sample ====================\n");
+
+    { /* GET the KV value */
+        /* get the "boot_count" KV value */
+        //fdb_kv_get_blob(kvdb, "boot_count", fdb_blob_make(&blob, &boot_count, sizeof(boot_count)));
+        fdb_blob_make(&blob, &boot_count, sizeof(boot_count));
+        fdb_kv_get_blob(kvdb, "boot_count", &blob); // fdb_blob_make(&blob, &boot_count, sizeof(boot_count)));
+        
+        /* the blob.saved.len is more than 0 when get the value successful */
+        if (blob.saved.len > 0) {
+            FDB_INFO("get the 'boot_count' value is %d\n", boot_count);
+        } else {
+            FDB_INFO("get the 'boot_count' failed\n");
+        }
+    }
+
+    { /* CHANGE the KV value */
+        /* increase the boot count */
+        boot_count ++;
+        /* change the "boot_count" KV's value */
+        fdb_kv_set_blob(kvdb, "boot_count", fdb_blob_make(&blob, &boot_count, sizeof(boot_count)));
+        FDB_INFO("set the 'boot_count' value to %d\n", boot_count);
+    }
+
+    FDB_INFO("===========================================================\n");
+}
+
+#endif /* FDB_USING_KVDB */

+ 63 - 0
package/flashdb/kvdb_type_blob_sample.c

@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2020, Armink, <armink.ztl@gmail.com>
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/**
+ * @file
+ * @brief blob KV samples.
+ *
+ * Key-Value Database blob type KV feature samples
+ */
+
+#include "flashdb.h"
+
+#ifdef FDB_USING_KVDB
+
+#define FDB_LOG_TAG "[sample][kvdb][blob]"
+
+void kvdb_type_blob_sample(fdb_kvdb_t kvdb)
+{
+    struct fdb_blob blob;
+
+    FDB_INFO("==================== kvdb_type_blob_sample ====================\n");
+
+    { /* CREATE new Key-Value */
+        int temp_data = 36;
+
+        /* It will create new KV node when "temp" KV not in database.
+         * fdb_blob_make: It's a blob make function, and it will return the blob when make finish.
+         */
+        fdb_kv_set_blob(kvdb, "temp", fdb_blob_make(&blob, &temp_data, sizeof(temp_data)));
+        FDB_INFO("create the 'temp' blob KV, value is: %d\n", temp_data);
+    }
+
+    { /* GET the KV value */
+        int temp_data = 0;
+
+        /* get the "temp" KV value */
+        fdb_kv_get_blob(kvdb, "temp", fdb_blob_make(&blob, &temp_data, sizeof(temp_data)));
+        /* the blob.saved.len is more than 0 when get the value successful */
+        if (blob.saved.len > 0) {
+            FDB_INFO("get the 'temp' value is: %d\n", temp_data);
+        }
+    }
+
+    { /* CHANGE the KV value */
+        int temp_data = 38;
+
+        /* change the "temp" KV's value to 38 */
+        fdb_kv_set_blob(kvdb, "temp", fdb_blob_make(&blob, &temp_data, sizeof(temp_data)));
+        FDB_INFO("set 'temp' value to %d\n", temp_data);
+    }
+
+    { /* DELETE the KV by name */
+        fdb_kv_del(kvdb, "temp");
+        FDB_INFO("delete the 'temp' finish\n");
+    }
+
+    FDB_INFO("===========================================================\n");
+}
+
+#endif /* FDB_USING_KVDB */

+ 63 - 0
package/flashdb/kvdb_type_string_sample.c

@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2020, Armink, <armink.ztl@gmail.com>
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/**
+ * @file
+ * @brief string KV samples.
+ *
+ * Key-Value Database string type KV feature samples source file.
+ */
+
+#include "flashdb.h"
+#include <string.h>
+
+#ifdef FDB_USING_KVDB
+
+#define FDB_LOG_TAG "[sample][kvdb][string]"
+
+void kvdb_type_string_sample(fdb_kvdb_t kvdb)
+{
+    FDB_INFO("==================== kvdb_type_string_sample ====================\n");
+
+    { /* CREATE new Key-Value */
+        char temp_data[10] = "36C";
+
+        /* It will create new KV node when "temp" KV not in database. */
+        fdb_kv_set(kvdb, "temp", temp_data);
+        FDB_INFO("create the 'temp' string KV, value is: %s\n", temp_data);
+    }
+
+    { /* GET the KV value */
+        char *return_value, temp_data[10] = { 0 };
+
+        /* Get the "temp" KV value.
+         * NOTE: The return value saved in fdb_kv_get's buffer. Please copy away as soon as possible.
+         */
+        return_value = fdb_kv_get(kvdb, "temp");
+        /* the return value is NULL when get the value failed */
+        if (return_value != NULL) {
+            strncpy(temp_data, return_value, sizeof(temp_data));
+            FDB_INFO("get the 'temp' value is: %s\n", temp_data);
+        }
+    }
+
+    { /* CHANGE the KV value */
+        char temp_data[10] = "38C";
+
+        /* change the "temp" KV's value to "38.1" */
+        fdb_kv_set(kvdb, "temp", temp_data);
+        FDB_INFO("set 'temp' value to %s\n", temp_data);
+    }
+
+    { /* DELETE the KV by name */
+        fdb_kv_del(kvdb, "temp");
+        FDB_INFO("delete the 'temp' finish\n");
+    }
+
+    FDB_INFO("===========================================================\n");
+}
+
+#endif /* FDB_USING_KVDB */

+ 129 - 0
package/flashdb/tsdb_sample.c

@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 2020, Armink, <armink.ztl@gmail.com>
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/**
+ * @file
+ * @brief TSDB samples.
+ *
+ * Time series log (like TSDB) feature samples source file.
+ *
+ * TSL is time series log, the TSDB saved many TSLs.
+ */
+
+#include "flashdb.h"
+#include <string.h>
+
+#ifdef FDB_USING_TSDB
+
+#define FDB_LOG_TAG "[sample][tsdb]"
+
+#ifdef FDB_USING_TIMESTAMP_64BIT
+#define __PRITS "ld"
+#else
+#define __PRITS "d"
+#endif
+
+struct env_status {
+    int temp;
+    int humi;
+};
+
+static bool query_cb(fdb_tsl_t tsl, void *arg);
+static bool query_by_time_cb(fdb_tsl_t tsl, void *arg);
+static bool set_status_cb(fdb_tsl_t tsl, void *arg);
+
+void tsdb_sample(fdb_tsdb_t tsdb)
+{
+    struct fdb_blob blob;
+
+    FDB_INFO("==================== tsdb_sample ====================\n");
+
+    { /* APPEND new TSL (time series log) */
+        struct env_status status;
+
+        /* append new log to TSDB */
+        status.temp = 36;
+        status.humi = 85;
+        fdb_tsl_append(tsdb, fdb_blob_make(&blob, &status, sizeof(status)));
+        FDB_INFO("append the new status.temp (%d) and status.humi (%d)\n", status.temp, status.humi);
+
+        status.temp = 38;
+        status.humi = 90;
+        fdb_tsl_append(tsdb, fdb_blob_make(&blob, &status, sizeof(status)));
+        FDB_INFO("append the new status.temp (%d) and status.humi (%d)\n", status.temp, status.humi);
+    }
+
+    { /* QUERY the TSDB */
+        /* query all TSL in TSDB by iterator */
+        fdb_tsl_iter(tsdb, query_cb, tsdb);
+    }
+
+    { /* QUERY the TSDB by time */
+        /* prepare query time (from 1970-01-01 00:00:00 to 2020-05-05 00:00:00) */
+        struct tm tm_from = { .tm_year = 1970 - 1900, .tm_mon = 0, .tm_mday = 1, .tm_hour = 0, .tm_min = 0, .tm_sec = 0 };
+        struct tm tm_to = { .tm_year = 2020 - 1900, .tm_mon = 4, .tm_mday = 5, .tm_hour = 0, .tm_min = 0, .tm_sec = 0 };
+        time_t from_time = mktime(&tm_from), to_time = mktime(&tm_to);
+        size_t count;
+        /* query all TSL in TSDB by time */
+        fdb_tsl_iter_by_time(tsdb, from_time, to_time, query_by_time_cb, tsdb);
+        /* query all FDB_TSL_WRITE status TSL's count in TSDB by time */
+        count = fdb_tsl_query_count(tsdb, from_time, to_time, FDB_TSL_WRITE);
+        FDB_INFO("query count is: %zu\n", count);
+    }
+
+    { /* SET the TSL status */
+        /* Change the TSL status by iterator or time iterator
+         * set_status_cb: the change operation will in this callback
+         *
+         * NOTE: The actions to modify the state must be in order.
+         *       like: FDB_TSL_WRITE -> FDB_TSL_USER_STATUS1 -> FDB_TSL_DELETED -> FDB_TSL_USER_STATUS2
+         *       The intermediate states can also be ignored.
+         *       such as: FDB_TSL_WRITE -> FDB_TSL_DELETED
+         */
+        fdb_tsl_iter(tsdb, set_status_cb, tsdb);
+    }
+
+    FDB_INFO("===========================================================\n");
+}
+
+static bool query_cb(fdb_tsl_t tsl, void *arg)
+{
+    struct fdb_blob blob;
+    // rbg/kcf
+    struct env_status status={0};
+    fdb_tsdb_t db = arg;
+
+    fdb_blob_read((fdb_db_t) db, fdb_tsl_to_blob(tsl, fdb_blob_make(&blob, &status, sizeof(status))));
+    FDB_INFO("[query_cb] queried a TSL: time: %ld, temp: %d, humi: %d\n", tsl->time, status.temp, status.humi);
+
+    return false;
+}
+
+static bool query_by_time_cb(fdb_tsl_t tsl, void *arg)
+{
+    struct fdb_blob blob;
+    // rbg/kcf 
+    struct env_status status={0};
+    fdb_tsdb_t db = arg;
+
+    fdb_blob_read((fdb_db_t) db, fdb_tsl_to_blob(tsl, fdb_blob_make(&blob, &status, sizeof(status))));
+    // rbg/kcf
+    FDB_INFO("[query_by_time_cb] queried a TSL: time: %ld, temp: %d, humi: %d\n", tsl->time, status.temp, status.humi);
+
+    return false;
+}
+
+static bool set_status_cb(fdb_tsl_t tsl, void *arg)
+{
+    fdb_tsdb_t db = arg;
+
+    FDB_INFO("set the TSL (time %ld) status from %d to %d\n", tsl->time, tsl->status, FDB_TSL_USER_STATUS1);
+    fdb_tsl_set_status(db, tsl, FDB_TSL_USER_STATUS1);
+
+    return false;
+}
+
+#endif /* FDB_USING_TSDB */

+ 1 - 1
port/linux/CMakeLists.txt

@@ -1,5 +1,5 @@
 cmake_minimum_required(VERSION 3.0.0)
-add_compile_options(-Wall -Wextra -Wno-unused-parameter -Wno-sign-conversion -Wno-write-strings -Wno-implicit-fallthrough -Wno-sign-compare -Wno-cast-function-type -Wno-unused-function)
+add_compile_options(-Wall -Wextra -Wno-unused-parameter -Wno-sign-conversion -Wno-write-strings -Wno-implicit-fallthrough -Wno-sign-compare -Wno-cast-function-type -Wno-unused-function -Wno-missing-braces -Wno-missing-field-initializers)
 OPTION(PIKA_CONFIG_ENABLE "pika config enable" OFF)
 IF(PIKA_CONFIG_ENABLE)
     ADD_DEFINITIONS(-DPIKA_CONFIG_ENABLE)

+ 0 - 139
port/linux/boot/demo06-pikamain/main.c

@@ -4,147 +4,8 @@
 #include "PikaVM.h"
 #include "pikaScript.h"
 
-//#define PIKA_USING_FLASHDB 1
-#if PIKA_USING_FLASHDB
-//#include <pthread.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include "flashdb.h"
-#define FDB_LOG_TAG "[main]"
-//static pthread_mutex_t kv_locker, ts_locker;
-static uint32_t boot_count = 0;
-static time_t boot_time[10] = {0, 1, 2, 3};
-/* default KV nodes */
-static struct fdb_default_kv_node default_kv_table[] = {
-        {"username", "armink", 0}, /* string KV */
-        {"password", "123456", 0}, /* string KV */
-        {"boot_count", &boot_count, sizeof(boot_count)}, /* int type KV */
-        {"boot_time", &boot_time, sizeof(boot_time)},    /* int array type KV */
-};
-/* KVDB object */
-static struct fdb_kvdb kvdb = { 0 };
-/* TSDB object */
-struct fdb_tsdb tsdb = { 0 };
-/* counts for simulated timestamp */
-static int counts = 0;
-
-extern void kvdb_basic_sample(fdb_kvdb_t kvdb);
-extern void kvdb_type_string_sample(fdb_kvdb_t kvdb);
-extern void kvdb_type_blob_sample(fdb_kvdb_t kvdb);
-extern void tsdb_sample(fdb_tsdb_t tsdb);
-
-/*
-static void lock(fdb_db_t db)
-{
-    pthread_mutex_lock((pthread_mutex_t *)db->user_data);
-}
-
-static void unlock(fdb_db_t db)
-{
-    pthread_mutex_unlock((pthread_mutex_t *)db->user_data);
-}
-*/
-static fdb_time_t get_time(void)
-{
-    return time(NULL);
-}
-#endif
-
 int main(int argc, char* argv[]) {
-#if PIKA_USING_FLASHDB
-        __platform_printf(" FDB_LOG_TAG :%s \r\n", FDB_LOG_TAG);
-    fdb_err_t result;
-    bool file_mode = true;
-    uint32_t sec_size = 4096, db_size = sec_size * 4;
-#define FDB_USING_KVDB 1
-#ifdef FDB_USING_KVDB
-    { /* KVDB Sample */
-        struct fdb_default_kv default_kv;
-
-        default_kv.kvs = default_kv_table;
-        default_kv.num = sizeof(default_kv_table) / sizeof(default_kv_table[0]);
-        /* set the lock and unlock function if you want */
-  //      pthread_mutex_init(&kv_locker, NULL);
-  //      fdb_kvdb_control(&kvdb, FDB_KVDB_CTRL_SET_LOCK, (void *)lock);
-  //      fdb_kvdb_control(&kvdb, FDB_KVDB_CTRL_SET_UNLOCK, (void *)unlock);
-        /* set the sector and database max size */
-        fdb_kvdb_control(&kvdb, FDB_KVDB_CTRL_SET_SEC_SIZE, &sec_size);
-        fdb_kvdb_control(&kvdb, FDB_KVDB_CTRL_SET_MAX_SIZE, &db_size);
-        /* enable file mode */
-        fdb_kvdb_control(&kvdb, FDB_KVDB_CTRL_SET_FILE_MODE, &file_mode);
-        /* create database directory */
-        mkdir("fdb_kvdb1", 0777);
-        /* Key-Value database initialization
-         *
-         *       &kvdb: database object
-         *       "env": database name
-         * "fdb_kvdb1": The flash partition name base on FAL. Please make sure it's in FAL partition table.
-         *              Please change to YOUR partition name.
-         * &default_kv: The default KV nodes. It will auto add to KVDB when first initialize successfully.
-         *  &kv_locker: The locker object.
-         */
-        //result = fdb_kvdb_init(&kvdb, "env", "fdb_kvdb1", &default_kv, &kv_locker);
-        result = fdb_kvdb_init(&kvdb, "env", "fdb_kvdb1", &default_kv, NULL);
-
-        if (result != FDB_NO_ERR) {
-            return -1;
-        }
-
-        /* run basic KV samples */
-        kvdb_basic_sample(&kvdb);
-        /* run string KV samples */
-        kvdb_type_string_sample(&kvdb);
-        /* run blob KV samples */
-        kvdb_type_blob_sample(&kvdb);
-    }
-#endif /* FDB_USING_KVDB */
-
-//#define FDB_USING_TSDB 1
-#ifdef FDB_USING_TSDB
-    { /* TSDB Sample */
-        /* set the lock and unlock function if you want */
-     //   pthread_mutex_init(&ts_locker, NULL);
-    //    fdb_tsdb_control(&tsdb, FDB_TSDB_CTRL_SET_LOCK, (void *)lock);
-      //  fdb_tsdb_control(&tsdb, FDB_TSDB_CTRL_SET_UNLOCK, (void *)unlock);
-        /* set the sector and database max size */
-        fdb_tsdb_control(&tsdb, FDB_TSDB_CTRL_SET_SEC_SIZE, &sec_size);
-        fdb_tsdb_control(&tsdb, FDB_TSDB_CTRL_SET_MAX_SIZE, &db_size);
-        /* enable file mode */
-        fdb_tsdb_control(&tsdb, FDB_TSDB_CTRL_SET_FILE_MODE, &file_mode);
-        /* create database directory */
-        mkdir("fdb_tsdb1", 0777);
-        /* Time series database initialization
-         *
-         *       &tsdb: database object
-         *       "log": database name
-         * "fdb_tsdb1": The flash partition name base on FAL. Please make sure it's in FAL partition table.
-         *              Please change to YOUR partition name.
-         *    get_time: The get current timestamp function.
-         *         128: maximum length of each log
-         *   ts_locker: The locker object.
-         */
-        //result = fdb_tsdb_init(&tsdb, "log", "fdb_tsdb1", get_time, 128, &ts_locker);
-        result = fdb_tsdb_init(&tsdb, "log", "fdb_tsdb1", get_time, 128, NULL);
-        
-        /* read last saved time for simulated timestamp */
-        fdb_tsdb_control(&tsdb, FDB_TSDB_CTRL_GET_LAST_TIME, &counts);
-
-        if (result != FDB_NO_ERR) {
-            return -1;
-        }
-
-        /* run TSDB sample */
-        tsdb_sample(&tsdb);
-    }
-#endif /* FDB_USING_TSDB */
-
-
-#else
-        __platform_printf(" FDB NOT INUSE \r\n");
-#endif
-
     if (1 == argc) {
-        
         __platform_printf("======[ rbg/kcf test ]======\r\n");
         PikaObj* pikaMain = pikaScriptInit();
         pikaScriptShell(pikaMain);

+ 0 - 1
port/linux/package/pikascript/main.py

@@ -6,7 +6,6 @@ import time
 import struct
 
 import flashdb
-import flashdb_utest
 
 print('hello pikapython!')
 mem = PikaStdLib.MemChecker()

+ 147 - 140
port/linux/package/pikascript/pikascript-lib/flashdb/_flashdb_FlashDB.c

@@ -13,17 +13,17 @@
 #include "flashdb.h"
 #define FDB_LOG_TAG "[main]"
 
-bool g_kvdb_path_inited=false;
+bool g_kvdb_path_inited = false;
 
 /* TSDB object */
-//struct fdb_tsdb tsdb = { 0 };
+// struct fdb_tsdb tsdb = { 0 };
 /* counts for simulated timestamp */
-//static int counts = 0;
+// static int counts = 0;
 
-//extern void kvdb_basic_sample(fdb_kvdb_t kvdb);
-//extern void kvdb_type_string_sample(fdb_kvdb_t kvdb);
-//extern void kvdb_type_blob_sample(fdb_kvdb_t kvdb);
-//extern void tsdb_sample(fdb_tsdb_t tsdb);
+// extern void kvdb_basic_sample(fdb_kvdb_t kvdb);
+// extern void kvdb_type_string_sample(fdb_kvdb_t kvdb);
+// extern void kvdb_type_blob_sample(fdb_kvdb_t kvdb);
+// extern void tsdb_sample(fdb_tsdb_t tsdb);
 
 /*
 static void lock(fdb_db_t db)
@@ -36,8 +36,7 @@ static void unlock(fdb_db_t db)
     pthread_mutex_unlock((pthread_mutex_t *)db->user_data);
 }
 */
-static fdb_time_t get_time(void)
-{
+static fdb_time_t get_time(void) {
     return time(NULL);
 }
 #endif
@@ -48,13 +47,11 @@ typedef struct fdb_kvdb FDB_KVDB;
 typedef struct fdb_default_kv_node FDB_DEFAULT_KV_NODE;
 int g_def_kv_table_idx;
 
-void _flashdb_FlashDB___init__(PikaObj *self){
-
-}
+void _flashdb_FlashDB___init__(PikaObj* self) {}
 
 /*
-Arg* _flashdb_FlashDB_blob_make(PikaObj *self, Arg* blob, Arg* value_buf, int buf_len){
-  return NULL;
+Arg* _flashdb_FlashDB_blob_make(PikaObj *self, Arg* blob, Arg* value_buf, int
+buf_len){ return NULL;
 }
 
 Arg* _flashdb_FlashDB_blob_read(PikaObj *self, Arg* db, Arg* blob){
@@ -69,159 +66,169 @@ Arg* _flashdb_FlashDB_kv_get(PikaObj *self, Arg* kvdb, char* key){
   return NULL;
 }
 */
-PikaObj* _flashdb_FlashDB_kv_get_blob(PikaObj *self, PikaObj* kvdb_in, char* key, int size){
-  struct fdb_blob blob;
-  FDB_KVDB* kvdb = (FDB_KVDB*) obj_getPtr(kvdb_in, "kvdb");
-  blob.size = size;
-  uint8_t* buf = (uint8_t*) pikaMalloc(size+1);
-  if (!buf) {
-    printf("alloc fail\n");
-    return NULL;
-  }   
-  blob.buf = buf;
-  size_t len =fdb_kv_get_blob(kvdb, key, &blob); 
-  if (len != size) {
-    printf("size error\n");
-    pikaFree(buf, size+1);
-    return NULL;
-  }
-  PikaList* list = New_pikaList();
-  for (int i=0; i< len; i++) {
-    pikaList_append(list, arg_newInt(buf[i]));
-  }
-  pikaFree(buf, size+1);
-  return list;
+PikaObj* _flashdb_FlashDB_kv_get_blob(PikaObj* self,
+                                      PikaObj* kvdb_in,
+                                      char* key,
+                                      int size) {
+    struct fdb_blob blob;
+    FDB_KVDB* kvdb = (FDB_KVDB*)obj_getPtr(kvdb_in, "kvdb");
+    blob.size = size;
+    uint8_t* buf = (uint8_t*)pikaMalloc(size + 1);
+    if (!buf) {
+        printf("alloc fail\n");
+        return NULL;
+    }
+    blob.buf = buf;
+    size_t len = fdb_kv_get_blob(kvdb, key, &blob);
+    if (len != size) {
+        printf("size error\n");
+        pikaFree(buf, size + 1);
+        return NULL;
+    }
+    PikaList* list = New_pikaList();
+    for (int i = 0; i < len; i++) {
+        pikaList_append(list, arg_newInt(buf[i]));
+    }
+    pikaFree(buf, size + 1);
+    return list;
 }
 
-Arg* _flashdb_FlashDB_kv_get_obj(PikaObj *self, Arg* kvdb, char* key, Arg* kv){
-  return NULL;
+Arg* _flashdb_FlashDB_kv_get_obj(PikaObj* self, Arg* kvdb, char* key, Arg* kv) {
+    return NULL;
 }
 
-Arg* _flashdb_FlashDB_kv_iterate(PikaObj *self, Arg* kvdb, Arg* itr){
-  return NULL;
+Arg* _flashdb_FlashDB_kv_iterate(PikaObj* self, Arg* kvdb, Arg* itr) {
+    return NULL;
 }
 
-Arg* _flashdb_FlashDB_kv_iterator_init(PikaObj *self, Arg* kvdb, Arg* itr){
-  return NULL;
+Arg* _flashdb_FlashDB_kv_iterator_init(PikaObj* self, Arg* kvdb, Arg* itr) {
+    return NULL;
 }
 
-void _flashdb_FlashDB_kv_print(PikaObj *self, PikaObj* kvdb_in) {
-  FDB_KVDB* kvdb = (FDB_KVDB*) obj_getPtr(kvdb_in, "kvdb");
-  fdb_kv_print(kvdb);
+void _flashdb_FlashDB_kv_print(PikaObj* self, PikaObj* kvdb_in) {
+    FDB_KVDB* kvdb = (FDB_KVDB*)obj_getPtr(kvdb_in, "kvdb");
+    fdb_kv_print(kvdb);
 }
 
-int _flashdb_FlashDB_kv_set(PikaObj *self, Arg* kvdb, char* key, char* value){
-  return 0;
+int _flashdb_FlashDB_kv_set(PikaObj* self, Arg* kvdb, char* key, char* value) {
+    return 0;
 }
 
-int _flashdb_FlashDB_kv_set_blob(PikaObj *self, PikaObj* kvdb_in, char* key, Arg* blob_in){
-  fdb_err_t res=FDB_NO_ERR;
-  FDB_KVDB* kvdb = (FDB_KVDB*) obj_getPtr(kvdb_in, "kvdb");
+int _flashdb_FlashDB_kv_set_blob(PikaObj* self,
+                                 PikaObj* kvdb_in,
+                                 char* key,
+                                 Arg* blob_in) {
+    fdb_err_t res = FDB_NO_ERR;
+    FDB_KVDB* kvdb = (FDB_KVDB*)obj_getPtr(kvdb_in, "kvdb");
 
-  ArgType argt_blob_in = arg_getType(blob_in);
-  if (argt_blob_in != ARG_TYPE_BYTES) {
-    printf("blob must be bytes but got:%d", argt_blob_in);
-  }
-  size_t len = arg_getBytesSize(blob_in);
-  uint8_t* bytes = (uint8_t*) arg_getBytes(blob_in);
+    ArgType argt_blob_in = arg_getType(blob_in);
+    if (argt_blob_in != ARG_TYPE_BYTES) {
+        printf("blob must be bytes but got:%d", argt_blob_in);
+    }
+    size_t len = arg_getBytesSize(blob_in);
+    uint8_t* bytes = (uint8_t*)arg_getBytes(blob_in);
 
-  struct fdb_blob blob;
-  blob.size = len;
-  blob.buf = bytes;
+    struct fdb_blob blob;
+    blob.size = len;
+    blob.buf = bytes;
 
-  res = fdb_kv_set_blob(kvdb, key, &blob);
-  
-  return res;
-}
+    res = fdb_kv_set_blob(kvdb, key, &blob);
 
-int _flashdb_FlashDB_kv_set_default(PikaObj *self, Arg* kvdb){
-  return 0;
+    return res;
 }
 
-Arg* _flashdb_FlashDB_kv_to_blob(PikaObj *self, Arg* kv, Arg* blob){
-  return NULL;
+int _flashdb_FlashDB_kv_set_default(PikaObj* self, Arg* kvdb) {
+    return 0;
 }
 
-int _flashdb_FlashDB_kvdb_control(PikaObj *self, Arg* kvdb, int cmd, Arg* arg){
-  return 0;
+Arg* _flashdb_FlashDB_kv_to_blob(PikaObj* self, Arg* kv, Arg* blob) {
+    return NULL;
 }
 
-void _flashdb_FlashDB_kvdb_deinit(PikaObj *self, Arg* kvdb){
-
-}
-
-int32_t  _flashdb_foreach(PikaObj* self, Arg* keyEach, Arg* valEach, void* context) {
-  char* key = arg_getStr(keyEach);
-  ArgType argt_val = arg_getType(valEach);
-  struct fdb_default_kv_node* def_kv_table =(struct fdb_default_kv_node*) context;
-  if (argt_val==ARG_TYPE_STRING) {
-    char* val = arg_getStr(valEach);
-    
-    def_kv_table[g_def_kv_table_idx].key = strdup(key);
-    def_kv_table[g_def_kv_table_idx].value= strdup(val);
-    def_kv_table[g_def_kv_table_idx].value_len = 0;
-    g_def_kv_table_idx++;
-    
-  } else if (argt_val==ARG_TYPE_BYTES) {
-    size_t bytes_size = arg_getBytesSize(valEach);
-    uint8_t* bytes = arg_getBytes(valEach);
-    uint8_t* pbytes = (uint8_t*) malloc(bytes_size * sizeof(uint8_t));
-    memcpy(pbytes, bytes, bytes_size);
-    /*
-    for (size_t i=0; i < bytes_size; i++) {
-      printf("%02x", bytes[i]);
+int _flashdb_FlashDB_kvdb_control(PikaObj* self, Arg* kvdb, int cmd, Arg* arg) {
+    return 0;
+}
+
+void _flashdb_FlashDB_kvdb_deinit(PikaObj* self, Arg* kvdb) {}
+
+int32_t _flashdb_foreach(PikaObj* self,
+                         Arg* keyEach,
+                         Arg* valEach,
+                         void* context) {
+    char* key = arg_getStr(keyEach);
+    ArgType argt_val = arg_getType(valEach);
+    struct fdb_default_kv_node* def_kv_table =
+        (struct fdb_default_kv_node*)context;
+    if (argt_val == ARG_TYPE_STRING) {
+        char* val = arg_getStr(valEach);
+
+        def_kv_table[g_def_kv_table_idx].key = strdup(key);
+        def_kv_table[g_def_kv_table_idx].value = strdup(val);
+        def_kv_table[g_def_kv_table_idx].value_len = 0;
+        g_def_kv_table_idx++;
+
+    } else if (argt_val == ARG_TYPE_BYTES) {
+        size_t bytes_size = arg_getBytesSize(valEach);
+        uint8_t* bytes = arg_getBytes(valEach);
+        uint8_t* pbytes = (uint8_t*)malloc(bytes_size * sizeof(uint8_t));
+        memcpy(pbytes, bytes, bytes_size);
+        /*
+        for (size_t i=0; i < bytes_size; i++) {
+          printf("%02x", bytes[i]);
+        }
+        printf("\n");
+        */
+        def_kv_table[g_def_kv_table_idx].key = strdup(key);
+        def_kv_table[g_def_kv_table_idx].value = pbytes;
+        def_kv_table[g_def_kv_table_idx].value_len = bytes_size;
+        g_def_kv_table_idx++;
     }
-    printf("\n");
-    */
-    def_kv_table[g_def_kv_table_idx].key = strdup(key);
-    def_kv_table[g_def_kv_table_idx].value= pbytes;
-    def_kv_table[g_def_kv_table_idx].value_len = bytes_size;
-    g_def_kv_table_idx++;
-    
-  }
-  return 0;
-}
-
-
-PikaObj* _flashdb_FlashDB_kvdb_init(PikaObj *self, char* name, char* path, PikaObj* default_kv_in, Arg* user_data){
-  printf("kvdb_init \n");
-
-  fdb_err_t result;
-  if (!g_kvdb_path_inited) {
-    bool file_mode = true;
-    uint32_t sec_size = 4096, db_size = sec_size * 4;
+    return 0;
+}
+
+PikaObj* _flashdb_FlashDB_kvdb_init(PikaObj* self,
+                                    char* name,
+                                    char* path,
+                                    PikaObj* default_kv_in,
+                                    Arg* user_data) {
+    printf("kvdb_init \n");
+
+    fdb_err_t result;
+    if (!g_kvdb_path_inited) {
+        bool file_mode = true;
+        uint32_t sec_size = 4096, db_size = sec_size * 4;
+
+        fdb_kvdb_control(&g_kvdb, FDB_KVDB_CTRL_SET_SEC_SIZE, &sec_size);
+        fdb_kvdb_control(&g_kvdb, FDB_KVDB_CTRL_SET_MAX_SIZE, &db_size);
+        /* enable file mode */
+        fdb_kvdb_control(&g_kvdb, FDB_KVDB_CTRL_SET_FILE_MODE, &file_mode);
+        /* create database directory */
+        mkdir(path, 0777);
+        g_kvdb_path_inited = true;
+    }
+    // int len =pikaDict_getSize(default_kv_in);
 
-    fdb_kvdb_control(&g_kvdb, FDB_KVDB_CTRL_SET_SEC_SIZE, &sec_size);
-    fdb_kvdb_control(&g_kvdb, FDB_KVDB_CTRL_SET_MAX_SIZE, &db_size);
-    /* enable file mode */
-    fdb_kvdb_control(&g_kvdb, FDB_KVDB_CTRL_SET_FILE_MODE, &file_mode);
-    /* create database directory */
-    mkdir(path, 0777);
-    g_kvdb_path_inited=true;
-  }
-  // int len =pikaDict_getSize(default_kv_in);
+    struct fdb_default_kv_node* def_kv_table =
+        (struct fdb_default_kv_node*)malloc(4 *
+                                            sizeof(struct fdb_default_kv_node));
+    g_def_kv_table_idx = 0;
+    pikaDict_forEach(default_kv_in, _flashdb_foreach, def_kv_table);
 
-  struct fdb_default_kv_node* def_kv_table =(struct fdb_default_kv_node*) malloc(4 * sizeof(struct fdb_default_kv_node));
-  g_def_kv_table_idx=0;
-  pikaDict_forEach(default_kv_in,  _flashdb_foreach, def_kv_table);
+    struct fdb_default_kv default_kv;
 
-  struct fdb_default_kv default_kv;
+    default_kv.kvs = def_kv_table;
+    default_kv.num = 4;
 
-  default_kv.kvs = def_kv_table;
-  default_kv.num = 4; 
+    result =
+        fdb_kvdb_init(&g_kvdb, strdup(name), strdup(path), &default_kv, NULL);
 
-  result = fdb_kvdb_init(&g_kvdb, strdup(name), strdup(path), &default_kv, NULL);
- 
-  if (result != FDB_NO_ERR)
-  {
-            return NULL;
-  }
+    if (result != FDB_NO_ERR) {
+        return NULL;
+    }
 
-  PikaObj* kvdb_obj = newNormalObj(New__flashdb_kvdb_t);
-  args_setStruct(kvdb_obj->list, "kvdb_struct", g_kvdb);
-  FDB_KVDB* pkvdb = args_getStruct(kvdb_obj->list, "kvdb_struct");
-  obj_setPtr(kvdb_obj, "kvdb", pkvdb);
-  return kvdb_obj;
+    PikaObj* kvdb_obj = newNormalObj(New__flashdb_kvdb_t);
+    args_setStruct(kvdb_obj->list, "kvdb_struct", g_kvdb);
+    FDB_KVDB* pkvdb = args_getStruct(kvdb_obj->list, "kvdb_struct");
+    obj_setPtr(kvdb_obj, "kvdb", pkvdb);
+    return kvdb_obj;
 }
-
-

+ 33 - 20
port/linux/package/pikascript/pikascript-lib/flashdb/fdb.c

@@ -22,8 +22,11 @@
 #error "Please defined the FDB_USING_FAL_MODE or FDB_USING_FILE_MODE macro"
 #endif
 
-fdb_err_t _fdb_init_ex(fdb_db_t db, const char *name, const char *path, fdb_db_type type, void *user_data)
-{
+fdb_err_t _fdb_init_ex(fdb_db_t db,
+                       const char* name,
+                       const char* path,
+                       fdb_db_type type,
+                       void* user_data) {
     FDB_ASSERT(db);
     FDB_ASSERT(name);
     FDB_ASSERT(path);
@@ -61,16 +64,22 @@ fdb_err_t _fdb_init_ex(fdb_db_t db, const char *name, const char *path, fdb_db_t
             return FDB_PART_NOT_FOUND;
         }
 
-        block_size = fal_flash_device_find(db->storage.part->flash_name)->blk_size;
+        block_size =
+            fal_flash_device_find(db->storage.part->flash_name)->blk_size;
         if (db->sec_size == 0) {
             db->sec_size = block_size;
         } else {
-	  // rbg/kcf
+            // rbg/kcf
             /* must be aligned with block size */
             if (db->sec_size % block_size != 0) {
-	      //     	FDB_INFO("Error: db sector size (%" PRIu32 ") MUST align with block size (%" PRIu32 ").\n", db->sec_size, block_size);
-	      	FDB_INFO("Error: db sector size (%lu) MUST align with block size (%u).\n", db->sec_size, block_size);		
-	      
+                //     	FDB_INFO("Error: db sector size (%" PRIu32 ") MUST align
+                //     with block size (%" PRIu32 ").\n", db->sec_size,
+                //     block_size);
+                FDB_INFO(
+                    "Error: db sector size (%lu) MUST align with block size "
+                    "(%u).\n",
+                    db->sec_size, block_size);
+
                 return FDB_INIT_FAILED;
             }
         }
@@ -83,36 +92,42 @@ fdb_err_t _fdb_init_ex(fdb_db_t db, const char *name, const char *path, fdb_db_t
     FDB_ASSERT((db->sec_size & (db->sec_size - 1)) == 0);
     /* must align with sector size */
     if (db->max_size % db->sec_size != 0) {
-        FDB_INFO("Error: db total size (%" PRIu32 ") MUST align with sector size (%" PRIu32 ").\n", db->max_size, db->sec_size);
+        FDB_INFO("Error: db total size (%" PRIu32
+                 ") MUST align with sector size (%" PRIu32 ").\n",
+                 db->max_size, db->sec_size);
         return FDB_INIT_FAILED;
     }
     /* must has more than or equal 2 sectors */
     if (db->max_size / db->sec_size < 2) {
-        FDB_INFO("Error: db MUST has more than or equal 2 sectors, current has %" PRIu32 " sector(s)\n", db->max_size / db->sec_size);
+        FDB_INFO(
+            "Error: db MUST has more than or equal 2 sectors, current has "
+            "%" PRIu32 " sector(s)\n",
+            db->max_size / db->sec_size);
         return FDB_INIT_FAILED;
     }
 
     return FDB_NO_ERR;
 }
 
-void _fdb_init_finish(fdb_db_t db, fdb_err_t result)
-{
+void _fdb_init_finish(fdb_db_t db, fdb_err_t result) {
     static bool log_is_show = false;
     if (result == FDB_NO_ERR) {
         db->init_ok = true;
         if (!log_is_show) {
             FDB_INFO("FlashDB V%s is initialize success.\n", FDB_SW_VERSION);
-            FDB_INFO("You can get the latest version on https://github.com/armink/FlashDB .\n");
+            FDB_INFO(
+                "You can get the latest version on "
+                "https://github.com/armink/FlashDB .\n");
             log_is_show = true;
         }
     } else if (!db->not_formatable) {
-        FDB_INFO("Error: %s (%s@%s) is initialize fail (%d).\n", db->type == FDB_DB_TYPE_KV ? "KVDB" : "TSDB",
-                db->name, _fdb_db_path(db), (int)result);
+        FDB_INFO("Error: %s (%s@%s) is initialize fail (%d).\n",
+                 db->type == FDB_DB_TYPE_KV ? "KVDB" : "TSDB", db->name,
+                 _fdb_db_path(db), (int)result);
     }
 }
 
-void _fdb_deinit(fdb_db_t db)
-{
+void _fdb_deinit(fdb_db_t db) {
     FDB_ASSERT(db);
 
     if (db->init_ok) {
@@ -135,16 +150,14 @@ void _fdb_deinit(fdb_db_t db)
     db->init_ok = false;
 }
 
-const char *_fdb_db_path(fdb_db_t db)
-{
+const char* _fdb_db_path(fdb_db_t db) {
     if (db->file_mode) {
 #ifdef FDB_USING_FILE_MODE
         return db->storage.dir;
 #else
         return NULL;
 #endif
-    }
-    else {
+    } else {
 #ifdef FDB_USING_FAL_MODE
         return db->storage.part->name;
 #else

+ 2 - 1
port/linux/package/pikascript/pikascript-lib/flashdb/fdb_cfg.h

@@ -16,7 +16,8 @@
 #define FDB_USING_KVDB
 
 #ifdef FDB_USING_KVDB
-/* Auto update KV to latest default when current KVDB version number is changed. @see fdb_kvdb.ver_num */
+/* Auto update KV to latest default when current KVDB version number is changed.
+ * @see fdb_kvdb.ver_num */
 /* #define FDB_KV_AUTO_UPDATE */
 #endif
 

+ 188 - 140
port/linux/package/pikascript/pikascript-lib/flashdb/fdb_def.h

@@ -19,22 +19,23 @@ extern "C" {
 #endif
 
 /* software version number */
-#define FDB_SW_VERSION                 "2.0.0"
-#define FDB_SW_VERSION_NUM             0x20000
+#define FDB_SW_VERSION "2.0.0"
+#define FDB_SW_VERSION_NUM 0x20000
 
 /* the KV max name length must less then it */
 #ifndef FDB_KV_NAME_MAX
-#define FDB_KV_NAME_MAX                64
+#define FDB_KV_NAME_MAX 64
 #endif
 
 /* the KV cache table size, it will improve KV search speed when using cache */
 #ifndef FDB_KV_CACHE_TABLE_SIZE
-#define FDB_KV_CACHE_TABLE_SIZE        64
+#define FDB_KV_CACHE_TABLE_SIZE 64
 #endif
 
-/* the sector cache table size, it will improve KV save speed when using cache */
+/* the sector cache table size, it will improve KV save speed when using cache
+ */
 #ifndef FDB_SECTOR_CACHE_TABLE_SIZE
-#define FDB_SECTOR_CACHE_TABLE_SIZE    8
+#define FDB_SECTOR_CACHE_TABLE_SIZE 8
 #endif
 
 #if (FDB_KV_CACHE_TABLE_SIZE > 0) && (FDB_SECTOR_CACHE_TABLE_SIZE > 0)
@@ -51,62 +52,94 @@ extern "C" {
 
 /* log function. default FDB_PRINT macro is printf() */
 #ifndef FDB_PRINT
-#define FDB_PRINT(...)                 pika_platform_printf(__VA_ARGS__)
+#define FDB_PRINT(...) pika_platform_printf(__VA_ARGS__)
 #endif
-#define FDB_LOG_PREFIX1()              FDB_PRINT("[FlashDB]" FDB_LOG_TAG)
-#define FDB_LOG_PREFIX2()              FDB_PRINT(" ")
-#define FDB_LOG_PREFIX()               FDB_LOG_PREFIX1();FDB_LOG_PREFIX2()
+#define FDB_LOG_PREFIX1() FDB_PRINT("[FlashDB]" FDB_LOG_TAG)
+#define FDB_LOG_PREFIX2() FDB_PRINT(" ")
+#define FDB_LOG_PREFIX() \
+    FDB_LOG_PREFIX1();   \
+    FDB_LOG_PREFIX2()
 #ifdef FDB_DEBUG_ENABLE
-#define FDB_DEBUG(...)                 FDB_LOG_PREFIX();FDB_PRINT("(%s:%d) ", __FILE__, __LINE__);FDB_PRINT(__VA_ARGS__)
+#define FDB_DEBUG(...)                         \
+    FDB_LOG_PREFIX();                          \
+    FDB_PRINT("(%s:%d) ", __FILE__, __LINE__); \
+    FDB_PRINT(__VA_ARGS__)
 #else
 #define FDB_DEBUG(...)
 #endif
 /* routine print function. Must be implement by user. */
-#define FDB_INFO(...)                  FDB_LOG_PREFIX();FDB_PRINT(__VA_ARGS__)
+#define FDB_INFO(...) \
+    FDB_LOG_PREFIX(); \
+    FDB_PRINT(__VA_ARGS__)
 /* assert for developer. */
-#define FDB_ASSERT(EXPR)                                                      \
-if (!(EXPR))                                                                  \
-{                                                                             \
-    FDB_INFO("(%s) has assert failed at %s.\n", #EXPR, __func__);             \
-    while (1);                                                                \
-}
-
-#define FDB_KVDB_CTRL_SET_SEC_SIZE     0x00             /**< set sector size control command, this change MUST before database initialization */
-#define FDB_KVDB_CTRL_GET_SEC_SIZE     0x01             /**< get sector size control command */
-#define FDB_KVDB_CTRL_SET_LOCK         0x02             /**< set lock function control command */
-#define FDB_KVDB_CTRL_SET_UNLOCK       0x03             /**< set unlock function control command */
-#define FDB_KVDB_CTRL_SET_FILE_MODE    0x09             /**< set file mode control command, this change MUST before database initialization */
-#define FDB_KVDB_CTRL_SET_MAX_SIZE     0x0A             /**< set database max size in file mode control command, this change MUST before database initialization */
-#define FDB_KVDB_CTRL_SET_NOT_FORMAT   0x0B             /**< set database NOT format mode control command, this change MUST before database initialization */
-
-#define FDB_TSDB_CTRL_SET_SEC_SIZE     0x00             /**< set sector size control command, this change MUST before database initialization */
-#define FDB_TSDB_CTRL_GET_SEC_SIZE     0x01             /**< get sector size control command */
-#define FDB_TSDB_CTRL_SET_LOCK         0x02             /**< set lock function control command */
-#define FDB_TSDB_CTRL_SET_UNLOCK       0x03             /**< set unlock function control command */
-#define FDB_TSDB_CTRL_SET_ROLLOVER     0x04             /**< set rollover control command, this change MUST after database initialization */
-#define FDB_TSDB_CTRL_GET_ROLLOVER     0x05             /**< get rollover control command */
-#define FDB_TSDB_CTRL_GET_LAST_TIME    0x06             /**< get last save time control command */
-#define FDB_TSDB_CTRL_SET_FILE_MODE    0x09             /**< set file mode control command, this change MUST before database initialization */
-#define FDB_TSDB_CTRL_SET_MAX_SIZE     0x0A             /**< set database max size in file mode control command, this change MUST before database initialization */
-#define FDB_TSDB_CTRL_SET_NOT_FORMAT   0x0B             /**< set database NOT formatable mode control command, this change MUST before database initialization */
+#define FDB_ASSERT(EXPR)                                              \
+    if (!(EXPR)) {                                                    \
+        FDB_INFO("(%s) has assert failed at %s.\n", #EXPR, __func__); \
+        while (1)                                                     \
+            ;                                                         \
+    }
+
+#define FDB_KVDB_CTRL_SET_SEC_SIZE                                     \
+    0x00 /**< set sector size control command, this change MUST before \
+            database initialization */
+#define FDB_KVDB_CTRL_GET_SEC_SIZE                                       \
+    0x01                            /**< get sector size control command \
+                                     */
+#define FDB_KVDB_CTRL_SET_LOCK 0x02 /**< set lock function control command */
+#define FDB_KVDB_CTRL_SET_UNLOCK \
+    0x03 /**< set unlock function control command */
+#define FDB_KVDB_CTRL_SET_FILE_MODE                                           \
+    0x09 /**< set file mode control command, this change MUST before database \
+            initialization */
+#define FDB_KVDB_CTRL_SET_MAX_SIZE                                            \
+    0x0A /**< set database max size in file mode control command, this change \
+            MUST before database initialization */
+#define FDB_KVDB_CTRL_SET_NOT_FORMAT                                         \
+    0x0B /**< set database NOT format mode control command, this change MUST \
+            before database initialization */
+
+#define FDB_TSDB_CTRL_SET_SEC_SIZE                                     \
+    0x00 /**< set sector size control command, this change MUST before \
+            database initialization */
+#define FDB_TSDB_CTRL_GET_SEC_SIZE                                       \
+    0x01                            /**< get sector size control command \
+                                     */
+#define FDB_TSDB_CTRL_SET_LOCK 0x02 /**< set lock function control command */
+#define FDB_TSDB_CTRL_SET_UNLOCK \
+    0x03 /**< set unlock function control command */
+#define FDB_TSDB_CTRL_SET_ROLLOVER                                          \
+    0x04 /**< set rollover control command, this change MUST after database \
+            initialization */
+#define FDB_TSDB_CTRL_GET_ROLLOVER 0x05 /**< get rollover control command */
+#define FDB_TSDB_CTRL_GET_LAST_TIME \
+    0x06 /**< get last save time control command */
+#define FDB_TSDB_CTRL_SET_FILE_MODE                                           \
+    0x09 /**< set file mode control command, this change MUST before database \
+            initialization */
+#define FDB_TSDB_CTRL_SET_MAX_SIZE                                            \
+    0x0A /**< set database max size in file mode control command, this change \
+            MUST before database initialization */
+#define FDB_TSDB_CTRL_SET_NOT_FORMAT                                        \
+    0x0B /**< set database NOT formatable mode control command, this change \
+            MUST before database initialization */
 
 #define FDB_USING_TIMESTAMP_64BIT
 #ifdef FDB_USING_TIMESTAMP_64BIT
-    typedef int64_t fdb_time_t;
+typedef int64_t fdb_time_t;
 #else
-    typedef int32_t fdb_time_t;
+typedef int32_t fdb_time_t;
 #endif /* FDB_USING_TIMESTAMP_64BIT */
 
 typedef fdb_time_t (*fdb_get_time)(void);
 
 struct fdb_default_kv_node {
-    char *key;
-    void *value;
+    char* key;
+    void* value;
     size_t value_len;
 };
 
 struct fdb_default_kv {
-    struct fdb_default_kv_node *kvs;
+    struct fdb_default_kv_node* kvs;
     size_t num;
 };
 
@@ -130,7 +163,7 @@ enum fdb_kv_status {
     FDB_KV_PRE_DELETE,
     FDB_KV_DELETED,
     FDB_KV_ERR_HDR,
-#define FDB_KV_STATUS_NUM                        6
+#define FDB_KV_STATUS_NUM 6
 };
 typedef enum fdb_kv_status fdb_kv_status_t;
 
@@ -141,49 +174,54 @@ enum fdb_tsl_status {
     FDB_TSL_USER_STATUS1,
     FDB_TSL_DELETED,
     FDB_TSL_USER_STATUS2,
-#define FDB_TSL_STATUS_NUM                       6
+#define FDB_TSL_STATUS_NUM 6
 };
 typedef enum fdb_tsl_status fdb_tsl_status_t;
 
 /* key-value node object */
 struct fdb_kv {
-    fdb_kv_status_t status;                      /**< node status, @see fdb_kv_status_t */
-    bool crc_is_ok;                              /**< node CRC32 check is OK */
-    uint8_t name_len;                            /**< name length */
-    uint32_t magic;                              /**< magic word(`K`, `V`, `4`, `0`) */
-    // uint32_t len;                                /**< node total length (header + name + value), must align by FDB_WRITE_GRAN */
-    unsigned long len;                                /**< node total length (header + name + value), must align by FDB_WRITE_GRAN */
-    uint32_t value_len;                          /**< value length */
-    char name[FDB_KV_NAME_MAX];                  /**< name */
+    fdb_kv_status_t status; /**< node status, @see fdb_kv_status_t */
+    bool crc_is_ok;         /**< node CRC32 check is OK */
+    uint8_t name_len;       /**< name length */
+    uint32_t magic;         /**< magic word(`K`, `V`, `4`, `0`) */
+    // uint32_t len;                                /**< node total length
+    // (header + name + value), must align by FDB_WRITE_GRAN */
+    unsigned long len;  /**< node total length (header + name + value), must
+                           align by FDB_WRITE_GRAN */
+    uint32_t value_len; /**< value length */
+    char name[FDB_KV_NAME_MAX]; /**< name */
     struct {
-        uint32_t start;                          /**< node start address */
-        uint32_t value;                          /**< value start address */
+        uint32_t start; /**< node start address */
+        uint32_t value; /**< value start address */
     } addr;
 };
-typedef struct fdb_kv *fdb_kv_t;
+typedef struct fdb_kv* fdb_kv_t;
 
 struct fdb_kv_iterator {
-    struct fdb_kv curr_kv;                       /**< Current KV we get from the iterator */
-    uint32_t iterated_cnt;                       /**< How many KVs have we iterated already */
-    size_t iterated_obj_bytes;                   /**< Total storage size of KVs we have iterated. */
-    size_t iterated_value_bytes;                 /**< Total value size of KVs we have iterated. */
-    uint32_t sector_addr;                        /**< Current sector address we're iterating. DO NOT touch it. */
-    uint32_t traversed_len;                      /**< Traversed sector total length. */
+    struct fdb_kv curr_kv; /**< Current KV we get from the iterator */
+    uint32_t iterated_cnt; /**< How many KVs have we iterated already */
+    size_t
+        iterated_obj_bytes; /**< Total storage size of KVs we have iterated. */
+    size_t
+        iterated_value_bytes; /**< Total value size of KVs we have iterated. */
+    uint32_t sector_addr;   /**< Current sector address we're iterating. DO NOT
+                               touch it. */
+    uint32_t traversed_len; /**< Traversed sector total length. */
 };
-typedef struct fdb_kv_iterator *fdb_kv_iterator_t;
+typedef struct fdb_kv_iterator* fdb_kv_iterator_t;
 
 /* time series log node object */
 struct fdb_tsl {
-    fdb_tsl_status_t status;                     /**< node status, @see fdb_log_status_t */
-    fdb_time_t time;                             /**< node timestamp */
-    uint32_t log_len;                            /**< log length, must align by FDB_WRITE_GRAN */
+    fdb_tsl_status_t status; /**< node status, @see fdb_log_status_t */
+    fdb_time_t time;         /**< node timestamp */
+    uint32_t log_len;        /**< log length, must align by FDB_WRITE_GRAN */
     struct {
-        uint32_t index;                          /**< node index address */
-        uint32_t log;                            /**< log data address */
+        uint32_t index; /**< node index address */
+        uint32_t log;   /**< log data address */
     } addr;
 };
-typedef struct fdb_tsl *fdb_tsl_t;
-typedef bool (*fdb_tsl_cb)(fdb_tsl_t tsl, void *arg);
+typedef struct fdb_tsl* fdb_tsl_t;
+typedef bool (*fdb_tsl_cb)(fdb_tsl_t tsl, void* arg);
 
 typedef enum {
     FDB_DB_TYPE_KV,
@@ -196,7 +234,7 @@ enum fdb_sector_store_status {
     FDB_SECTOR_STORE_EMPTY,
     FDB_SECTOR_STORE_USING,
     FDB_SECTOR_STORE_FULL,
-#define FDB_SECTOR_STORE_STATUS_NUM              4
+#define FDB_SECTOR_STORE_STATUS_NUM 4
 };
 typedef enum fdb_sector_store_status fdb_sector_store_status_t;
 
@@ -206,93 +244,102 @@ enum fdb_sector_dirty_status {
     FDB_SECTOR_DIRTY_FALSE,
     FDB_SECTOR_DIRTY_TRUE,
     FDB_SECTOR_DIRTY_GC,
-#define FDB_SECTOR_DIRTY_STATUS_NUM              4
+#define FDB_SECTOR_DIRTY_STATUS_NUM 4
 };
 typedef enum fdb_sector_dirty_status fdb_sector_dirty_status_t;
 
 /* KVDB section information */
 struct kvdb_sec_info {
-    bool check_ok;                               /**< sector header check is OK */
+    bool check_ok; /**< sector header check is OK */
     struct {
-        fdb_sector_store_status_t store;         /**< sector store status @see fdb_sector_store_status_t */
-        fdb_sector_dirty_status_t dirty;         /**< sector dirty status @see sector_dirty_status_t */
+        fdb_sector_store_status_t
+            store; /**< sector store status @see fdb_sector_store_status_t */
+        fdb_sector_dirty_status_t
+            dirty; /**< sector dirty status @see sector_dirty_status_t */
     } status;
-    uint32_t addr;                               /**< sector start address */
-    uint32_t magic;                              /**< magic word(`E`, `F`, `4`, `0`) */
-    uint32_t combined;                           /**< the combined next sector number, 0xFFFFFFFF: not combined */
-    size_t remain;                               /**< remain size */
-    uint32_t empty_kv;                           /**< the next empty KV node start address */
+    uint32_t addr;     /**< sector start address */
+    uint32_t magic;    /**< magic word(`E`, `F`, `4`, `0`) */
+    uint32_t combined; /**< the combined next sector number, 0xFFFFFFFF: not
+                          combined */
+    size_t remain;     /**< remain size */
+    uint32_t empty_kv; /**< the next empty KV node start address */
 };
-typedef struct kvdb_sec_info *kv_sec_info_t;
+typedef struct kvdb_sec_info* kv_sec_info_t;
 
 /* TSDB section information */
 struct tsdb_sec_info {
-    bool check_ok;                               /**< sector header check is OK */
-    fdb_sector_store_status_t status;            /**< sector store status @see fdb_sector_store_status_t */
-    uint32_t addr;                               /**< sector start address */
-    uint32_t magic;                              /**< magic word(`T`, `S`, `L`, `0`) */
-    fdb_time_t start_time;                       /**< the first start node's timestamp, 0xFFFFFFFF: unused */
-    fdb_time_t end_time;                         /**< the last end node's timestamp, 0xFFFFFFFF: unused */
-    uint32_t end_idx;                            /**< the last end node's index, 0xFFFFFFFF: unused */
-    fdb_tsl_status_t end_info_stat[2];           /**< the last end node's info status */
-    size_t remain;                               /**< remain size */
-    uint32_t empty_idx;                          /**< the next empty node index address */
-    uint32_t empty_data;                         /**< the next empty node's data end address */
+    bool check_ok; /**< sector header check is OK */
+    fdb_sector_store_status_t
+        status;     /**< sector store status @see fdb_sector_store_status_t */
+    uint32_t addr;  /**< sector start address */
+    uint32_t magic; /**< magic word(`T`, `S`, `L`, `0`) */
+    fdb_time_t
+        start_time; /**< the first start node's timestamp, 0xFFFFFFFF: unused */
+    fdb_time_t
+        end_time;     /**< the last end node's timestamp, 0xFFFFFFFF: unused */
+    uint32_t end_idx; /**< the last end node's index, 0xFFFFFFFF: unused */
+    fdb_tsl_status_t end_info_stat[2]; /**< the last end node's info status */
+    size_t remain;                     /**< remain size */
+    uint32_t empty_idx;                /**< the next empty node index address */
+    uint32_t empty_data; /**< the next empty node's data end address */
 };
-typedef struct tsdb_sec_info *tsdb_sec_info_t;
+typedef struct tsdb_sec_info* tsdb_sec_info_t;
 
 struct kv_cache_node {
-    uint16_t name_crc;                           /**< KV name's CRC32 low 16bit value */
-    uint16_t active;                             /**< KV node access active degree */
-    uint32_t addr;                               /**< KV node address */
+    uint16_t name_crc; /**< KV name's CRC32 low 16bit value */
+    uint16_t active;   /**< KV node access active degree */
+    uint32_t addr;     /**< KV node address */
 };
-typedef struct kv_cache_node *kv_cache_node_t;
+typedef struct kv_cache_node* kv_cache_node_t;
 
 struct sector_cache_node {
-    uint32_t addr;                               /**< sector start address */
-    uint32_t empty_addr;                         /**< sector empty address */
+    uint32_t addr;       /**< sector start address */
+    uint32_t empty_addr; /**< sector empty address */
 };
-typedef struct sector_cache_node *sector_cache_node_t;
+typedef struct sector_cache_node* sector_cache_node_t;
 
 /* database structure */
-typedef struct fdb_db *fdb_db_t;
+typedef struct fdb_db* fdb_db_t;
 struct fdb_db {
-    const char *name;                            /**< database name */
-    fdb_db_type type;                            /**< database type */
+    const char* name; /**< database name */
+    fdb_db_type type; /**< database type */
     union {
 #ifdef FDB_USING_FAL_MODE
-        const struct fal_partition *part;        /**< flash partition for saving database */
+        const struct fal_partition*
+            part; /**< flash partition for saving database */
 #endif
 #ifdef FDB_USING_FILE_MODE
-        const char *dir;                         /**< directory path for saving database */
+        const char* dir; /**< directory path for saving database */
 #endif
     } storage;
-    uint32_t sec_size;                           /**< flash section size. It's a multiple of block size */
-    uint32_t max_size;                           /**< database max size. It's a multiple of section size */
-    uint32_t oldest_addr;                        /**< the oldest sector start address */
-    bool init_ok;                                /**< initialized successfully */
-    bool file_mode;                              /**< is file mode, default is false */
-    bool not_formatable;                         /**< is can NOT be formated mode, default is false */
+    uint32_t sec_size; /**< flash section size. It's a multiple of block size */
+    uint32_t
+        max_size; /**< database max size. It's a multiple of section size */
+    uint32_t oldest_addr; /**< the oldest sector start address */
+    bool init_ok;         /**< initialized successfully */
+    bool file_mode;       /**< is file mode, default is false */
+    bool not_formatable;  /**< is can NOT be formated mode, default is false */
 #ifdef FDB_USING_FILE_MODE
 #if defined(FDB_USING_FILE_POSIX_MODE)
-    int cur_file;                                /**< current file object */
+    int cur_file; /**< current file object */
 #elif defined(FDB_USING_FILE_LIBC_MODE)
-    FILE *cur_file;                              /**< current file object */
+    FILE* cur_file; /**< current file object */
 #endif
-    uint32_t cur_sec;                            /**< current operate sector address  */
+    uint32_t cur_sec; /**< current operate sector address  */
 #endif
-    void (*lock)(fdb_db_t db);                   /**< lock the database operate */
-    void (*unlock)(fdb_db_t db);                 /**< unlock the database operate */
+    void (*lock)(fdb_db_t db);   /**< lock the database operate */
+    void (*unlock)(fdb_db_t db); /**< unlock the database operate */
 
-    void *user_data;
+    void* user_data;
 };
 
 /* KVDB structure */
 struct fdb_kvdb {
-    struct fdb_db parent;                        /**< inherit from fdb_db */
-    struct fdb_default_kv default_kvs;           /**< default KV */
-    bool gc_request;                             /**< request a GC check */
-    bool in_recovery_check;                      /**< is in recovery check status when first reboot */
+    struct fdb_db parent;              /**< inherit from fdb_db */
+    struct fdb_default_kv default_kvs; /**< default KV */
+    bool gc_request;                   /**< request a GC check */
+    bool
+        in_recovery_check; /**< is in recovery check status when first reboot */
     struct fdb_kv cur_kv;
     struct kvdb_sec_info cur_sector;
     bool last_is_complete_del;
@@ -300,46 +347,47 @@ struct fdb_kvdb {
 #ifdef FDB_KV_USING_CACHE
     /* KV cache table */
     struct kv_cache_node kv_cache_table[FDB_KV_CACHE_TABLE_SIZE];
-    /* sector cache table, it caching the sector info which status is current using */
+    /* sector cache table, it caching the sector info which status is current
+     * using */
     struct sector_cache_node sector_cache_table[FDB_SECTOR_CACHE_TABLE_SIZE];
 #endif /* FDB_KV_USING_CACHE */
 
 #ifdef FDB_KV_AUTO_UPDATE
-    uint32_t ver_num;                            /**< setting version number for update */
+    uint32_t ver_num; /**< setting version number for update */
 #endif
 
-    void *user_data;
+    void* user_data;
 };
-typedef struct fdb_kvdb *fdb_kvdb_t;
+typedef struct fdb_kvdb* fdb_kvdb_t;
 
 /* TSDB structure */
 struct fdb_tsdb {
-    struct fdb_db parent;                        /**< inherit from fdb_db */
-    struct tsdb_sec_info cur_sec;                /**< current using sector */
-    fdb_time_t last_time;                        /**< last TSL timestamp */
-    fdb_get_time get_time;                       /**< the current timestamp get function */
-    size_t max_len;                              /**< the maximum length of each log */
-    bool rollover;                               /**< the oldest data will rollover by newest data, default is true */
-
-    void *user_data;
+    struct fdb_db parent;         /**< inherit from fdb_db */
+    struct tsdb_sec_info cur_sec; /**< current using sector */
+    fdb_time_t last_time;         /**< last TSL timestamp */
+    fdb_get_time get_time;        /**< the current timestamp get function */
+    size_t max_len;               /**< the maximum length of each log */
+    bool rollover; /**< the oldest data will rollover by newest data, default is
+                      true */
+
+    void* user_data;
 };
-typedef struct fdb_tsdb *fdb_tsdb_t;
+typedef struct fdb_tsdb* fdb_tsdb_t;
 
 /* blob structure */
 struct fdb_blob {
-    void *buf;                                   /**< blob data buffer */
-    size_t size;                                 /**< blob data buffer size */
+    void* buf;   /**< blob data buffer */
+    size_t size; /**< blob data buffer size */
     struct {
-        uint32_t meta_addr;                      /**< saved KV or TSL index address */
-        uint32_t addr;                           /**< blob data saved address */
-        size_t len;                              /**< blob data saved length */
+        uint32_t meta_addr; /**< saved KV or TSL index address */
+        uint32_t addr;      /**< blob data saved address */
+        size_t len;         /**< blob data saved length */
     } saved;
 };
-typedef struct fdb_blob *fdb_blob_t;
+typedef struct fdb_blob* fdb_blob_t;
 
 #ifdef __cplusplus
 }
 #endif
 
 #endif /* _FDB_DEF_H_ */
-

+ 44 - 39
port/linux/package/pikascript/pikascript-lib/flashdb/fdb_file.c

@@ -14,18 +14,21 @@
 
 #ifdef FDB_USING_FILE_MODE
 
-#define DB_PATH_MAX            256
+#define DB_PATH_MAX 256
 
-static void get_db_file_path(fdb_db_t db, uint32_t addr, char *path, size_t size)
-{
-#define DB_NAME_MAX            8
+static void get_db_file_path(fdb_db_t db,
+                             uint32_t addr,
+                             char* path,
+                             size_t size) {
+#define DB_NAME_MAX 8
 
     /* from db_name.fdb.0 to db_name.fdb.n */
     char file_name[DB_NAME_MAX + 4 + 10];
     uint32_t sec_addr = FDB_ALIGN_DOWN(addr, db->sec_size);
     int index = sec_addr / db->sec_size;
 
-    snprintf(file_name, sizeof(file_name), "%.*s.fdb.%d", DB_NAME_MAX, db->name, index);
+    snprintf(file_name, sizeof(file_name), "%.*s.fdb.%d", DB_NAME_MAX, db->name,
+             index);
     if (strlen(db->storage.dir) + 1 + strlen(file_name) >= size) {
         /* path is too long */
         FDB_ASSERT(0)
@@ -41,8 +44,7 @@ static void get_db_file_path(fdb_db_t db, uint32_t addr, char *path, size_t size
 #include <unistd.h>
 #endif
 
-static int open_db_file(fdb_db_t db, uint32_t addr, bool clean)
-{
+static int open_db_file(fdb_db_t db, uint32_t addr, bool clean) {
     uint32_t sec_addr = FDB_ALIGN_DOWN(addr, db->sec_size);
     int fd = db->cur_file;
     char path[DB_PATH_MAX];
@@ -59,8 +61,7 @@ static int open_db_file(fdb_db_t db, uint32_t addr, bool clean)
             fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0777);
             if (fd <= 0) {
                 FDB_INFO("Error: open (%s) file failed.\n", path);
-            }
-            else {
+            } else {
                 close(fd);
                 fd = -1;
             }
@@ -74,14 +75,15 @@ static int open_db_file(fdb_db_t db, uint32_t addr, bool clean)
     return db->cur_file;
 }
 
-fdb_err_t _fdb_file_read(fdb_db_t db, uint32_t addr, void *buf, size_t size)
-{
+fdb_err_t _fdb_file_read(fdb_db_t db, uint32_t addr, void* buf, size_t size) {
     fdb_err_t result = FDB_NO_ERR;
     int fd = open_db_file(db, addr, false);
     if (fd > 0) {
-        /* get the offset address is relative to the start of the current file */
+        /* get the offset address is relative to the start of the current file
+         */
         addr = addr % db->sec_size;
-        if ((lseek(fd, addr, SEEK_SET) != addr) || (read(fd, buf, size) != size))
+        if ((lseek(fd, addr, SEEK_SET) != addr) ||
+            (read(fd, buf, size) != size))
             result = FDB_READ_ERR;
     } else {
         result = FDB_READ_ERR;
@@ -89,16 +91,21 @@ fdb_err_t _fdb_file_read(fdb_db_t db, uint32_t addr, void *buf, size_t size)
     return result;
 }
 
-fdb_err_t _fdb_file_write(fdb_db_t db, uint32_t addr, const void *buf, size_t size, bool sync)
-{
+fdb_err_t _fdb_file_write(fdb_db_t db,
+                          uint32_t addr,
+                          const void* buf,
+                          size_t size,
+                          bool sync) {
     fdb_err_t result = FDB_NO_ERR;
     int fd = open_db_file(db, addr, false);
     if (fd > 0) {
-        /* get the offset address is relative to the start of the current file */
+        /* get the offset address is relative to the start of the current file
+         */
         addr = addr % db->sec_size;
-        if ((lseek(fd, addr, SEEK_SET) != addr) || (write(fd, buf, size) != size))
+        if ((lseek(fd, addr, SEEK_SET) != addr) ||
+            (write(fd, buf, size) != size))
             result = FDB_WRITE_ERR;
-        if(sync) {
+        if (sync) {
             fsync(fd);
         }
     } else {
@@ -107,8 +114,7 @@ fdb_err_t _fdb_file_write(fdb_db_t db, uint32_t addr, const void *buf, size_t si
     return result;
 }
 
-fdb_err_t _fdb_file_erase(fdb_db_t db, uint32_t addr, size_t size)
-{
+fdb_err_t _fdb_file_erase(fdb_db_t db, uint32_t addr, size_t size) {
     fdb_err_t result = FDB_NO_ERR;
     int fd = open_db_file(db, addr, true);
     if (fd > 0) {
@@ -116,8 +122,7 @@ fdb_err_t _fdb_file_erase(fdb_db_t db, uint32_t addr, size_t size)
         uint8_t buf[BUF_SIZE];
         size_t i;
         lseek(fd, 0, SEEK_SET);
-        for (i = 0; i * BUF_SIZE < size; i++)
-        {
+        for (i = 0; i * BUF_SIZE < size; i++) {
             memset(buf, 0xFF, BUF_SIZE);
             write(fd, buf, BUF_SIZE);
         }
@@ -130,8 +135,7 @@ fdb_err_t _fdb_file_erase(fdb_db_t db, uint32_t addr, size_t size)
     return result;
 }
 #elif defined(FDB_USING_FILE_LIBC_MODE)
-static FILE *open_db_file(fdb_db_t db, uint32_t addr, bool clean)
-{
+static FILE* open_db_file(fdb_db_t db, uint32_t addr, bool clean) {
     uint32_t sec_addr = FDB_ALIGN_DOWN(addr, db->sec_size);
 
     if (sec_addr != db->cur_sec || db->cur_file == NULL || clean) {
@@ -161,13 +165,13 @@ static FILE *open_db_file(fdb_db_t db, uint32_t addr, bool clean)
     return db->cur_file;
 }
 
-fdb_err_t _fdb_file_read(fdb_db_t db, uint32_t addr, void *buf, size_t size)
-{
+fdb_err_t _fdb_file_read(fdb_db_t db, uint32_t addr, void* buf, size_t size) {
     fdb_err_t result = FDB_NO_ERR;
-    FILE *fp = open_db_file(db, addr, false);
+    FILE* fp = open_db_file(db, addr, false);
     if (fp) {
         addr = addr % db->sec_size;
-        if ((fseek(fp, addr, SEEK_SET) != 0) || (fread(buf, size, 1, fp) != size))
+        if ((fseek(fp, addr, SEEK_SET) != 0) ||
+            (fread(buf, size, 1, fp) != size))
             result = FDB_READ_ERR;
     } else {
         result = FDB_READ_ERR;
@@ -175,15 +179,19 @@ fdb_err_t _fdb_file_read(fdb_db_t db, uint32_t addr, void *buf, size_t size)
     return result;
 }
 
-fdb_err_t _fdb_file_write(fdb_db_t db, uint32_t addr, const void *buf, size_t size, bool sync)
-{
+fdb_err_t _fdb_file_write(fdb_db_t db,
+                          uint32_t addr,
+                          const void* buf,
+                          size_t size,
+                          bool sync) {
     fdb_err_t result = FDB_NO_ERR;
-    FILE *fp = open_db_file(db, addr, false);
+    FILE* fp = open_db_file(db, addr, false);
     if (fp) {
         addr = addr % db->sec_size;
-        if ((fseek(fp, addr, SEEK_SET) != 0) || (fwrite(buf, size, 1, fp) != size))
+        if ((fseek(fp, addr, SEEK_SET) != 0) ||
+            (fwrite(buf, size, 1, fp) != size))
             result = FDB_READ_ERR;
-        if(sync) {
+        if (sync) {
             fflush(fp);
         }
     } else {
@@ -193,18 +201,16 @@ fdb_err_t _fdb_file_write(fdb_db_t db, uint32_t addr, const void *buf, size_t si
     return result;
 }
 
-fdb_err_t _fdb_file_erase(fdb_db_t db, uint32_t addr, size_t size)
-{
+fdb_err_t _fdb_file_erase(fdb_db_t db, uint32_t addr, size_t size) {
     fdb_err_t result = FDB_NO_ERR;
 
-    FILE *fp = open_db_file(db, addr, true);
+    FILE* fp = open_db_file(db, addr, true);
     if (fp != NULL) {
 #define BUF_SIZE 32
         uint8_t buf[BUF_SIZE];
         size_t i;
         fseek(fp, 0, SEEK_SET);
-        for (i = 0; i * BUF_SIZE < size; i++)
-        {
+        for (i = 0; i * BUF_SIZE < size; i++) {
             memset(buf, 0xFF, BUF_SIZE);
             fwrite(buf, BUF_SIZE, 1, fp);
         }
@@ -219,4 +225,3 @@ fdb_err_t _fdb_file_erase(fdb_db_t db, uint32_t addr, size_t size)
 #endif /* defined(FDB_USING_FILE_LIBC_MODE) */
 
 #endif /* FDB_USING_FILE_MODE */
-

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 318 - 216
port/linux/package/pikascript/pikascript-lib/flashdb/fdb_kvdb.c


+ 42 - 20
port/linux/package/pikascript/pikascript-lib/flashdb/fdb_low_lvl.h

@@ -16,52 +16,74 @@
 #include "fdb_def.h"
 
 #if (FDB_WRITE_GRAN == 1)
-#define FDB_STATUS_TABLE_SIZE(status_number)       ((status_number * FDB_WRITE_GRAN + 7)/8)
+#define FDB_STATUS_TABLE_SIZE(status_number) \
+    ((status_number * FDB_WRITE_GRAN + 7) / 8)
 #else
-#define FDB_STATUS_TABLE_SIZE(status_number)       (((status_number - 1) * FDB_WRITE_GRAN + 7)/8)
+#define FDB_STATUS_TABLE_SIZE(status_number) \
+    (((status_number - 1) * FDB_WRITE_GRAN + 7) / 8)
 #endif
 
 /* the data is erased */
-#define FDB_BYTE_ERASED                           0xFF
+#define FDB_BYTE_ERASED 0xFF
 /* the data is written */
-#define FDB_BYTE_WRITTEN                          0x00
+#define FDB_BYTE_WRITTEN 0x00
 
 /* Return the most contiguous size aligned at specified width. RT_ALIGN(13, 4)
  * would return 16.
  */
-#define FDB_ALIGN(size, align)                    (((size) + (align) - 1) & ~((align) - 1))
+#define FDB_ALIGN(size, align) (((size) + (align)-1) & ~((align)-1))
 /* align by write granularity */
-#define FDB_WG_ALIGN(size)                        (FDB_ALIGN(size, (FDB_WRITE_GRAN + 7)/8))
+#define FDB_WG_ALIGN(size) (FDB_ALIGN(size, (FDB_WRITE_GRAN + 7) / 8))
 /**
  * Return the down number of aligned at specified width. RT_ALIGN_DOWN(13, 4)
  * would return 12.
  */
-#define FDB_ALIGN_DOWN(size, align)               ((size) & ~((align) - 1))
+#define FDB_ALIGN_DOWN(size, align) ((size) & ~((align)-1))
 /* align down by write granularity */
-#define FDB_WG_ALIGN_DOWN(size)                   (FDB_ALIGN_DOWN(size, (FDB_WRITE_GRAN + 7)/8))
+#define FDB_WG_ALIGN_DOWN(size) (FDB_ALIGN_DOWN(size, (FDB_WRITE_GRAN + 7) / 8))
 
-#define FDB_STORE_STATUS_TABLE_SIZE               FDB_STATUS_TABLE_SIZE(FDB_SECTOR_STORE_STATUS_NUM)
-#define FDB_DIRTY_STATUS_TABLE_SIZE               FDB_STATUS_TABLE_SIZE(FDB_SECTOR_DIRTY_STATUS_NUM)
+#define FDB_STORE_STATUS_TABLE_SIZE \
+    FDB_STATUS_TABLE_SIZE(FDB_SECTOR_STORE_STATUS_NUM)
+#define FDB_DIRTY_STATUS_TABLE_SIZE \
+    FDB_STATUS_TABLE_SIZE(FDB_SECTOR_DIRTY_STATUS_NUM)
 
 /* the data is unused */
-#if (FDB_BYTE_ERASED  == 0xFF)
-#define FDB_DATA_UNUSED                      0xFFFFFFFF
+#if (FDB_BYTE_ERASED == 0xFF)
+#define FDB_DATA_UNUSED 0xFFFFFFFF
 #else
-#define FDB_DATA_UNUSED                      0x00000000
+#define FDB_DATA_UNUSED 0x00000000
 #endif
 
 fdb_err_t _fdb_kv_load(fdb_kvdb_t db);
-size_t _fdb_set_status(uint8_t status_table[], size_t status_num, size_t status_index);
+size_t _fdb_set_status(uint8_t status_table[],
+                       size_t status_num,
+                       size_t status_index);
 size_t _fdb_get_status(uint8_t status_table[], size_t status_num);
 uint32_t _fdb_continue_ff_addr(fdb_db_t db, uint32_t start, uint32_t end);
-fdb_err_t _fdb_init_ex(fdb_db_t db, const char *name, const char *part_name, fdb_db_type type, void *user_data);
+fdb_err_t _fdb_init_ex(fdb_db_t db,
+                       const char* name,
+                       const char* part_name,
+                       fdb_db_type type,
+                       void* user_data);
 void _fdb_init_finish(fdb_db_t db, fdb_err_t result);
 void _fdb_deinit(fdb_db_t db);
-const char *_fdb_db_path(fdb_db_t db);
-fdb_err_t _fdb_write_status(fdb_db_t db, uint32_t addr, uint8_t status_table[], size_t status_num, size_t status_index, bool sync);
-size_t _fdb_read_status(fdb_db_t db, uint32_t addr, uint8_t status_table[], size_t total_num);
-fdb_err_t _fdb_flash_read(fdb_db_t db, uint32_t addr, void *buf, size_t size);
+const char* _fdb_db_path(fdb_db_t db);
+fdb_err_t _fdb_write_status(fdb_db_t db,
+                            uint32_t addr,
+                            uint8_t status_table[],
+                            size_t status_num,
+                            size_t status_index,
+                            bool sync);
+size_t _fdb_read_status(fdb_db_t db,
+                        uint32_t addr,
+                        uint8_t status_table[],
+                        size_t total_num);
+fdb_err_t _fdb_flash_read(fdb_db_t db, uint32_t addr, void* buf, size_t size);
 fdb_err_t _fdb_flash_erase(fdb_db_t db, uint32_t addr, size_t size);
-fdb_err_t _fdb_flash_write(fdb_db_t db, uint32_t addr, const void *buf, size_t size, bool sync);
+fdb_err_t _fdb_flash_write(fdb_db_t db,
+                           uint32_t addr,
+                           const void* buf,
+                           size_t size,
+                           bool sync);
 
 #endif /* _FDB_LOW_LVL_H_ */

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 328 - 231
port/linux/package/pikascript/pikascript-lib/flashdb/fdb_tsdb.c


+ 77 - 53
port/linux/package/pikascript/pikascript-lib/flashdb/fdb_utils.c

@@ -18,8 +18,7 @@
 
 #define FDB_LOG_TAG "[utils]"
 
-static const uint32_t crc32_table[] =
-{
+static const uint32_t crc32_table[] = {
     0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
     0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
     0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
@@ -62,8 +61,7 @@ static const uint32_t crc32_table[] =
     0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
     0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
     0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
-    0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
-};
+    0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d};
 
 /**
  * Calculate the CRC32 value of a memory buffer.
@@ -74,11 +72,10 @@ static const uint32_t crc32_table[] =
  *
  * @return calculated CRC32 value
  */
-uint32_t fdb_calc_crc32(uint32_t crc, const void *buf, size_t size)
-{
-    const uint8_t *p;
+uint32_t fdb_calc_crc32(uint32_t crc, const void* buf, size_t size) {
+    const uint8_t* p;
 
-    p = (const uint8_t *)buf;
+    p = (const uint8_t*)buf;
     crc = crc ^ ~0U;
 
     while (size--) {
@@ -88,22 +85,28 @@ uint32_t fdb_calc_crc32(uint32_t crc, const void *buf, size_t size)
     return crc ^ ~0U;
 }
 
-size_t _fdb_set_status(uint8_t status_table[], size_t status_num, size_t status_index)
-{
+size_t _fdb_set_status(uint8_t status_table[],
+                       size_t status_num,
+                       size_t status_index) {
     size_t byte_index = ~0UL;
     /*
-     * | write garn |       status0       |       status1       |      status2         |       status3      |
+     * | write garn |       status0       |       status1       |      status2
+     * |       status3      |
      * ------------------------------------------------------------------------------------------------------
-     * |    1bit    | 0xFF                | 0x7F                |  0x3F                |  0x1F
+     * |    1bit    | 0xFF                | 0x7F                |  0x3F |  0x1F
      * ------------------------------------------------------------------------------------------------------
-     * |    8bit    | 0xFF FF FF          | 0x00 FF FF          |  0x00 00 FF          |  0x00 00 00
+     * |    8bit    | 0xFF FF FF          | 0x00 FF FF          |  0x00 00 FF |
+     * 0x00 00 00
      * ------------------------------------------------------------------------------------------------------
-     * |   32bit    | 0xFFFFFFFF FFFFFFFF | 0x00FFFFFF FFFFFFFF |  0x00FFFFFF 00FFFFFF |  0x00FFFFFF 00FFFFFF
-     * |            | 0xFFFFFFFF          | 0xFFFFFFFF          |  0xFFFFFFFF          |  0x00FFFFFF
+     * |   32bit    | 0xFFFFFFFF FFFFFFFF | 0x00FFFFFF FFFFFFFF |  0x00FFFFFF
+     * 00FFFFFF |  0x00FFFFFF 00FFFFFF |            | 0xFFFFFFFF          |
+     * 0xFFFFFFFF          |  0xFFFFFFFF          |  0x00FFFFFF
      * ------------------------------------------------------------------------------------------------------
-     * |            | 0xFFFFFFFF FFFFFFFF | 0x00FFFFFF FFFFFFFF |  0x00FFFFFF FFFFFFFF |  0x00FFFFFF FFFFFFFF
-     * |   64bit    | 0xFFFFFFFF FFFFFFFF | 0xFFFFFFFF FFFFFFFF |  0x00FFFFFF FFFFFFFF |  0x00FFFFFF FFFFFFFF
-     * |            | 0xFFFFFFFF FFFFFFFF | 0xFFFFFFFF FFFFFFFF |  0xFFFFFFFF FFFFFFFF |  0x00FFFFFF FFFFFFFF
+     * |            | 0xFFFFFFFF FFFFFFFF | 0x00FFFFFF FFFFFFFF |  0x00FFFFFF
+     * FFFFFFFF |  0x00FFFFFF FFFFFFFF |   64bit    | 0xFFFFFFFF FFFFFFFF |
+     * 0xFFFFFFFF FFFFFFFF |  0x00FFFFFF FFFFFFFF |  0x00FFFFFF FFFFFFFF | |
+     * 0xFFFFFFFF FFFFFFFF | 0xFFFFFFFF FFFFFFFF |  0xFFFFFFFF FFFFFFFF |
+     * 0x00FFFFFF FFFFFFFF
      */
     memset(status_table, FDB_BYTE_ERASED, FDB_STATUS_TABLE_SIZE(status_num));
     if (status_index > 0) {
@@ -123,17 +126,18 @@ size_t _fdb_set_status(uint8_t status_table[], size_t status_num, size_t status_
     return byte_index;
 }
 
-size_t _fdb_get_status(uint8_t status_table[], size_t status_num)
-{
+size_t _fdb_get_status(uint8_t status_table[], size_t status_num) {
     size_t i = 0, status_num_bak = --status_num;
 
-    while (status_num --) {
+    while (status_num--) {
         /* get the first 0 position from end address to start address */
 #if (FDB_WRITE_GRAN == 1)
-        if ((status_table[status_num / 8] & (0x80 >> (status_num % 8))) == 0x00) {
+        if ((status_table[status_num / 8] & (0x80 >> (status_num % 8))) ==
+            0x00) {
             break;
         }
-#else /*  (FDB_WRITE_GRAN == 8) ||  (FDB_WRITE_GRAN == 32) ||  (FDB_WRITE_GRAN == 64) */
+#else /*  (FDB_WRITE_GRAN == 8) ||  (FDB_WRITE_GRAN == 32) ||  (FDB_WRITE_GRAN \
+         == 64) */
         if (status_table[status_num * FDB_WRITE_GRAN / 8] == FDB_BYTE_WRITTEN) {
             break;
         }
@@ -144,8 +148,12 @@ size_t _fdb_get_status(uint8_t status_table[], size_t status_num)
     return status_num_bak - i;
 }
 
-fdb_err_t _fdb_write_status(fdb_db_t db, uint32_t addr, uint8_t status_table[], size_t status_num, size_t status_index, bool sync)
-{
+fdb_err_t _fdb_write_status(fdb_db_t db,
+                            uint32_t addr,
+                            uint8_t status_table[],
+                            size_t status_num,
+                            size_t status_index,
+                            bool sync) {
     fdb_err_t result = FDB_NO_ERR;
     size_t byte_index;
 
@@ -160,21 +168,29 @@ fdb_err_t _fdb_write_status(fdb_db_t db, uint32_t addr, uint8_t status_table[],
         return FDB_NO_ERR;
     }
 #if (FDB_WRITE_GRAN == 1)
-    result = _fdb_flash_write(db, addr + byte_index, (uint32_t *)&status_table[byte_index], 1, sync);
-#else /*  (FDB_WRITE_GRAN == 8) ||  (FDB_WRITE_GRAN == 32) ||  (FDB_WRITE_GRAN == 64) */
+    result = _fdb_flash_write(db, addr + byte_index,
+                              (uint32_t*)&status_table[byte_index], 1, sync);
+#else /*  (FDB_WRITE_GRAN == 8) ||  (FDB_WRITE_GRAN == 32) ||  (FDB_WRITE_GRAN \
+         == 64) */
     /* write the status by write granularity
-     * some flash (like stm32 onchip) NOT supported repeated write before erase */
-    result = _fdb_flash_write(db, addr + byte_index, (uint32_t *) &status_table[byte_index], FDB_WRITE_GRAN / 8, sync);
+     * some flash (like stm32 onchip) NOT supported repeated write before erase
+     */
+    result = _fdb_flash_write(db, addr + byte_index,
+                              (uint32_t*)&status_table[byte_index],
+                              FDB_WRITE_GRAN / 8, sync);
 #endif /* FDB_WRITE_GRAN == 1 */
 
     return result;
 }
 
-size_t _fdb_read_status(fdb_db_t db, uint32_t addr, uint8_t status_table[], size_t total_num)
-{
+size_t _fdb_read_status(fdb_db_t db,
+                        uint32_t addr,
+                        uint8_t status_table[],
+                        size_t total_num) {
     FDB_ASSERT(status_table);
 
-    _fdb_flash_read(db, addr, (uint32_t *) status_table, FDB_STATUS_TABLE_SIZE(total_num));
+    _fdb_flash_read(db, addr, (uint32_t*)status_table,
+                    FDB_STATUS_TABLE_SIZE(total_num));
 
     return _fdb_get_status(status_table, total_num);
 }
@@ -182,8 +198,7 @@ size_t _fdb_read_status(fdb_db_t db, uint32_t addr, uint8_t status_table[], size
 /*
  * find the continue 0xFF flash address to end address
  */
-uint32_t _fdb_continue_ff_addr(fdb_db_t db, uint32_t start, uint32_t end)
-{
+uint32_t _fdb_continue_ff_addr(fdb_db_t db, uint32_t start, uint32_t end) {
     uint8_t buf[32], last_data = FDB_BYTE_WRITTEN;
     size_t i, addr = start, read_size;
 
@@ -193,7 +208,7 @@ uint32_t _fdb_continue_ff_addr(fdb_db_t db, uint32_t start, uint32_t end)
         } else {
             read_size = end - start;
         }
-        _fdb_flash_read(db, start, (uint32_t *) buf, read_size);
+        _fdb_flash_read(db, start, (uint32_t*)buf, read_size);
         for (i = 0; i < read_size; i++) {
             if (last_data != FDB_BYTE_ERASED && buf[i] == FDB_BYTE_ERASED) {
                 addr = start + i;
@@ -218,9 +233,10 @@ uint32_t _fdb_continue_ff_addr(fdb_db_t db, uint32_t start, uint32_t end)
  *
  * @return new blob object
  */
-fdb_blob_t fdb_blob_make(fdb_blob_t blob, const void *value_buf, size_t buf_len)
-{
-    blob->buf = (void *)value_buf;
+fdb_blob_t fdb_blob_make(fdb_blob_t blob,
+                         const void* value_buf,
+                         size_t buf_len) {
+    blob->buf = (void*)value_buf;
     blob->size = buf_len;
 
     return blob;
@@ -234,14 +250,14 @@ fdb_blob_t fdb_blob_make(fdb_blob_t blob, const void *value_buf, size_t buf_len)
  *
  * @return read length
  */
-size_t fdb_blob_read(fdb_db_t db, fdb_blob_t blob)
-{
+size_t fdb_blob_read(fdb_db_t db, fdb_blob_t blob) {
     size_t read_len = blob->size;
 
     if (read_len > blob->saved.len) {
         read_len = blob->saved.len;
     }
-    if (_fdb_flash_read(db, blob->saved.addr, blob->buf, read_len) != FDB_NO_ERR) {
+    if (_fdb_flash_read(db, blob->saved.addr, blob->buf, read_len) !=
+        FDB_NO_ERR) {
         read_len = 0;
     }
 
@@ -249,13 +265,19 @@ size_t fdb_blob_read(fdb_db_t db, fdb_blob_t blob)
 }
 
 #ifdef FDB_USING_FILE_MODE
-extern fdb_err_t _fdb_file_read(fdb_db_t db, uint32_t addr, void *buf, size_t size);
-extern fdb_err_t _fdb_file_write(fdb_db_t db, uint32_t addr, const void *buf, size_t size, bool sync);
+extern fdb_err_t _fdb_file_read(fdb_db_t db,
+                                uint32_t addr,
+                                void* buf,
+                                size_t size);
+extern fdb_err_t _fdb_file_write(fdb_db_t db,
+                                 uint32_t addr,
+                                 const void* buf,
+                                 size_t size,
+                                 bool sync);
 extern fdb_err_t _fdb_file_erase(fdb_db_t db, uint32_t addr, size_t size);
 #endif /* FDB_USING_FILE_LIBC */
 
-fdb_err_t _fdb_flash_read(fdb_db_t db, uint32_t addr, void *buf, size_t size)
-{
+fdb_err_t _fdb_flash_read(fdb_db_t db, uint32_t addr, void* buf, size_t size) {
     fdb_err_t result = FDB_NO_ERR;
 
     if (db->file_mode) {
@@ -266,7 +288,8 @@ fdb_err_t _fdb_flash_read(fdb_db_t db, uint32_t addr, void *buf, size_t size)
 #endif
     } else {
 #ifdef FDB_USING_FAL_MODE
-        if (fal_partition_read(db->storage.part, addr, (uint8_t *) buf, size) < 0) {
+        if (fal_partition_read(db->storage.part, addr, (uint8_t*)buf, size) <
+            0) {
             result = FDB_READ_ERR;
         }
 #endif
@@ -275,8 +298,7 @@ fdb_err_t _fdb_flash_read(fdb_db_t db, uint32_t addr, void *buf, size_t size)
     return result;
 }
 
-fdb_err_t _fdb_flash_erase(fdb_db_t db, uint32_t addr, size_t size)
-{
+fdb_err_t _fdb_flash_erase(fdb_db_t db, uint32_t addr, size_t size) {
     fdb_err_t result = FDB_NO_ERR;
 
     if (db->file_mode) {
@@ -296,8 +318,11 @@ fdb_err_t _fdb_flash_erase(fdb_db_t db, uint32_t addr, size_t size)
     return result;
 }
 
-fdb_err_t _fdb_flash_write(fdb_db_t db, uint32_t addr, const void *buf, size_t size, bool sync)
-{
+fdb_err_t _fdb_flash_write(fdb_db_t db,
+                           uint32_t addr,
+                           const void* buf,
+                           size_t size,
+                           bool sync) {
     fdb_err_t result = FDB_NO_ERR;
 
     if (db->file_mode) {
@@ -308,13 +333,12 @@ fdb_err_t _fdb_flash_write(fdb_db_t db, uint32_t addr, const void *buf, size_t s
 #endif /* FDB_USING_FILE_MODE */
     } else {
 #ifdef FDB_USING_FAL_MODE
-        if (fal_partition_write(db->storage.part, addr, (uint8_t *)buf, size) < 0)
-        {
+        if (fal_partition_write(db->storage.part, addr, (uint8_t*)buf, size) <
+            0) {
             result = FDB_WRITE_ERR;
         }
 #endif
     }
 
     return result;
-
 }

+ 45 - 28
port/linux/package/pikascript/pikascript-lib/flashdb/flashdb.h

@@ -25,50 +25,67 @@
 
 #include "fdb_def.h"
 
-
 #ifdef __cplusplus
 extern "C" {
 #endif
 
 /* FlashDB database API */
-fdb_err_t fdb_kvdb_init   (fdb_kvdb_t db, const char *name, const char *path, struct fdb_default_kv *default_kv,
-        void *user_data);
-void      fdb_kvdb_control(fdb_kvdb_t db, int cmd, void *arg);
+fdb_err_t fdb_kvdb_init(fdb_kvdb_t db,
+                        const char* name,
+                        const char* path,
+                        struct fdb_default_kv* default_kv,
+                        void* user_data);
+void fdb_kvdb_control(fdb_kvdb_t db, int cmd, void* arg);
 fdb_err_t fdb_kvdb_deinit(fdb_kvdb_t db);
-fdb_err_t fdb_tsdb_init   (fdb_tsdb_t db, const char *name, const char *path, fdb_get_time get_time, size_t max_len,
-        void *user_data);
-void      fdb_tsdb_control(fdb_tsdb_t db, int cmd, void *arg);
+fdb_err_t fdb_tsdb_init(fdb_tsdb_t db,
+                        const char* name,
+                        const char* path,
+                        fdb_get_time get_time,
+                        size_t max_len,
+                        void* user_data);
+void fdb_tsdb_control(fdb_tsdb_t db, int cmd, void* arg);
 fdb_err_t fdb_tsdb_deinit(fdb_tsdb_t db);
 
 /* blob API */
-fdb_blob_t fdb_blob_make     (fdb_blob_t blob, const void *value_buf, size_t buf_len);
-size_t     fdb_blob_read     (fdb_db_t db, fdb_blob_t blob);
+fdb_blob_t fdb_blob_make(fdb_blob_t blob,
+                         const void* value_buf,
+                         size_t buf_len);
+size_t fdb_blob_read(fdb_db_t db, fdb_blob_t blob);
 
 /* Key-Value API like a KV DB */
-fdb_err_t         fdb_kv_set          (fdb_kvdb_t db, const char *key, const char *value);
-char             *fdb_kv_get          (fdb_kvdb_t db, const char *key);
-fdb_err_t         fdb_kv_set_blob     (fdb_kvdb_t db, const char *key, fdb_blob_t blob);
-size_t            fdb_kv_get_blob     (fdb_kvdb_t db, const char *key, fdb_blob_t blob);
-fdb_err_t         fdb_kv_del          (fdb_kvdb_t db, const char *key);
-fdb_kv_t          fdb_kv_get_obj      (fdb_kvdb_t db, const char *key, fdb_kv_t kv);
-fdb_blob_t        fdb_kv_to_blob      (fdb_kv_t   kv, fdb_blob_t blob);
-fdb_err_t         fdb_kv_set_default  (fdb_kvdb_t db);
-void              fdb_kv_print        (fdb_kvdb_t db);
+fdb_err_t fdb_kv_set(fdb_kvdb_t db, const char* key, const char* value);
+char* fdb_kv_get(fdb_kvdb_t db, const char* key);
+fdb_err_t fdb_kv_set_blob(fdb_kvdb_t db, const char* key, fdb_blob_t blob);
+size_t fdb_kv_get_blob(fdb_kvdb_t db, const char* key, fdb_blob_t blob);
+fdb_err_t fdb_kv_del(fdb_kvdb_t db, const char* key);
+fdb_kv_t fdb_kv_get_obj(fdb_kvdb_t db, const char* key, fdb_kv_t kv);
+fdb_blob_t fdb_kv_to_blob(fdb_kv_t kv, fdb_blob_t blob);
+fdb_err_t fdb_kv_set_default(fdb_kvdb_t db);
+void fdb_kv_print(fdb_kvdb_t db);
 fdb_kv_iterator_t fdb_kv_iterator_init(fdb_kvdb_t db, fdb_kv_iterator_t itr);
-bool              fdb_kv_iterate      (fdb_kvdb_t db, fdb_kv_iterator_t itr);
+bool fdb_kv_iterate(fdb_kvdb_t db, fdb_kv_iterator_t itr);
 
 /* Time series log API like a TSDB */
-fdb_err_t  fdb_tsl_append      (fdb_tsdb_t db, fdb_blob_t blob);
-void       fdb_tsl_iter        (fdb_tsdb_t db, fdb_tsl_cb cb, void *cb_arg);
-void       fdb_tsl_iter_reverse(fdb_tsdb_t db, fdb_tsl_cb cb, void *cb_arg);
-void       fdb_tsl_iter_by_time(fdb_tsdb_t db, fdb_time_t from, fdb_time_t to, fdb_tsl_cb cb, void *cb_arg);
-size_t     fdb_tsl_query_count (fdb_tsdb_t db, fdb_time_t from, fdb_time_t to, fdb_tsl_status_t status);
-fdb_err_t  fdb_tsl_set_status  (fdb_tsdb_t db, fdb_tsl_t tsl, fdb_tsl_status_t status);
-void       fdb_tsl_clean       (fdb_tsdb_t db);
-fdb_blob_t fdb_tsl_to_blob     (fdb_tsl_t tsl, fdb_blob_t blob);
+fdb_err_t fdb_tsl_append(fdb_tsdb_t db, fdb_blob_t blob);
+void fdb_tsl_iter(fdb_tsdb_t db, fdb_tsl_cb cb, void* cb_arg);
+void fdb_tsl_iter_reverse(fdb_tsdb_t db, fdb_tsl_cb cb, void* cb_arg);
+void fdb_tsl_iter_by_time(fdb_tsdb_t db,
+                          fdb_time_t from,
+                          fdb_time_t to,
+                          fdb_tsl_cb cb,
+                          void* cb_arg);
+size_t fdb_tsl_query_count(fdb_tsdb_t db,
+                           fdb_time_t from,
+                           fdb_time_t to,
+                           fdb_tsl_status_t status);
+fdb_err_t fdb_tsl_set_status(fdb_tsdb_t db,
+                             fdb_tsl_t tsl,
+                             fdb_tsl_status_t status);
+void fdb_tsl_clean(fdb_tsdb_t db);
+fdb_blob_t fdb_tsl_to_blob(fdb_tsl_t tsl, fdb_blob_t blob);
 
 /* fdb_utils.c */
-uint32_t   fdb_calc_crc32(uint32_t crc, const void *buf, size_t size);
+uint32_t fdb_calc_crc32(uint32_t crc, const void* buf, size_t size);
 
 #ifdef __cplusplus
 }

+ 10 - 7
port/linux/package/pikascript/pikascript-lib/flashdb/kvdb_basic_sample.c

@@ -18,8 +18,7 @@
 
 #define FDB_LOG_TAG "[sample][kvdb][basic]"
 
-void kvdb_basic_sample(fdb_kvdb_t kvdb)
-{
+void kvdb_basic_sample(fdb_kvdb_t kvdb) {
     struct fdb_blob blob;
     int boot_count = 0;
 
@@ -27,10 +26,13 @@ void kvdb_basic_sample(fdb_kvdb_t kvdb)
 
     { /* GET the KV value */
         /* get the "boot_count" KV value */
-        //fdb_kv_get_blob(kvdb, "boot_count", fdb_blob_make(&blob, &boot_count, sizeof(boot_count)));
+        // fdb_kv_get_blob(kvdb, "boot_count", fdb_blob_make(&blob, &boot_count,
+        // sizeof(boot_count)));
         fdb_blob_make(&blob, &boot_count, sizeof(boot_count));
-        fdb_kv_get_blob(kvdb, "boot_count", &blob); // fdb_blob_make(&blob, &boot_count, sizeof(boot_count)));
-        
+        fdb_kv_get_blob(
+            kvdb, "boot_count",
+            &blob);  // fdb_blob_make(&blob, &boot_count, sizeof(boot_count)));
+
         /* the blob.saved.len is more than 0 when get the value successful */
         if (blob.saved.len > 0) {
             FDB_INFO("get the 'boot_count' value is %d\n", boot_count);
@@ -41,9 +43,10 @@ void kvdb_basic_sample(fdb_kvdb_t kvdb)
 
     { /* CHANGE the KV value */
         /* increase the boot count */
-        boot_count ++;
+        boot_count++;
         /* change the "boot_count" KV's value */
-        fdb_kv_set_blob(kvdb, "boot_count", fdb_blob_make(&blob, &boot_count, sizeof(boot_count)));
+        fdb_kv_set_blob(kvdb, "boot_count",
+                        fdb_blob_make(&blob, &boot_count, sizeof(boot_count)));
         FDB_INFO("set the 'boot_count' value to %d\n", boot_count);
     }
 

+ 11 - 7
port/linux/package/pikascript/pikascript-lib/flashdb/kvdb_type_blob_sample.c

@@ -17,19 +17,21 @@
 
 #define FDB_LOG_TAG "[sample][kvdb][blob]"
 
-void kvdb_type_blob_sample(fdb_kvdb_t kvdb)
-{
+void kvdb_type_blob_sample(fdb_kvdb_t kvdb) {
     struct fdb_blob blob;
 
-    FDB_INFO("==================== kvdb_type_blob_sample ====================\n");
+    FDB_INFO(
+        "==================== kvdb_type_blob_sample ====================\n");
 
     { /* CREATE new Key-Value */
         int temp_data = 36;
 
         /* It will create new KV node when "temp" KV not in database.
-         * fdb_blob_make: It's a blob make function, and it will return the blob when make finish.
+         * fdb_blob_make: It's a blob make function, and it will return the blob
+         * when make finish.
          */
-        fdb_kv_set_blob(kvdb, "temp", fdb_blob_make(&blob, &temp_data, sizeof(temp_data)));
+        fdb_kv_set_blob(kvdb, "temp",
+                        fdb_blob_make(&blob, &temp_data, sizeof(temp_data)));
         FDB_INFO("create the 'temp' blob KV, value is: %d\n", temp_data);
     }
 
@@ -37,7 +39,8 @@ void kvdb_type_blob_sample(fdb_kvdb_t kvdb)
         int temp_data = 0;
 
         /* get the "temp" KV value */
-        fdb_kv_get_blob(kvdb, "temp", fdb_blob_make(&blob, &temp_data, sizeof(temp_data)));
+        fdb_kv_get_blob(kvdb, "temp",
+                        fdb_blob_make(&blob, &temp_data, sizeof(temp_data)));
         /* the blob.saved.len is more than 0 when get the value successful */
         if (blob.saved.len > 0) {
             FDB_INFO("get the 'temp' value is: %d\n", temp_data);
@@ -48,7 +51,8 @@ void kvdb_type_blob_sample(fdb_kvdb_t kvdb)
         int temp_data = 38;
 
         /* change the "temp" KV's value to 38 */
-        fdb_kv_set_blob(kvdb, "temp", fdb_blob_make(&blob, &temp_data, sizeof(temp_data)));
+        fdb_kv_set_blob(kvdb, "temp",
+                        fdb_blob_make(&blob, &temp_data, sizeof(temp_data)));
         FDB_INFO("set 'temp' value to %d\n", temp_data);
     }
 

+ 6 - 5
port/linux/package/pikascript/pikascript-lib/flashdb/kvdb_type_string_sample.c

@@ -18,9 +18,9 @@
 
 #define FDB_LOG_TAG "[sample][kvdb][string]"
 
-void kvdb_type_string_sample(fdb_kvdb_t kvdb)
-{
-    FDB_INFO("==================== kvdb_type_string_sample ====================\n");
+void kvdb_type_string_sample(fdb_kvdb_t kvdb) {
+    FDB_INFO(
+        "==================== kvdb_type_string_sample ====================\n");
 
     { /* CREATE new Key-Value */
         char temp_data[10] = "36C";
@@ -31,10 +31,11 @@ void kvdb_type_string_sample(fdb_kvdb_t kvdb)
     }
 
     { /* GET the KV value */
-        char *return_value, temp_data[10] = { 0 };
+        char *return_value, temp_data[10] = {0};
 
         /* Get the "temp" KV value.
-         * NOTE: The return value saved in fdb_kv_get's buffer. Please copy away as soon as possible.
+         * NOTE: The return value saved in fdb_kv_get's buffer. Please copy away
+         * as soon as possible.
          */
         return_value = fdb_kv_get(kvdb, "temp");
         /* the return value is NULL when get the value failed */

+ 43 - 26
port/linux/package/pikascript/pikascript-lib/flashdb/tsdb_sample.c

@@ -31,12 +31,11 @@ struct env_status {
     int humi;
 };
 
-static bool query_cb(fdb_tsl_t tsl, void *arg);
-static bool query_by_time_cb(fdb_tsl_t tsl, void *arg);
-static bool set_status_cb(fdb_tsl_t tsl, void *arg);
+static bool query_cb(fdb_tsl_t tsl, void* arg);
+static bool query_by_time_cb(fdb_tsl_t tsl, void* arg);
+static bool set_status_cb(fdb_tsl_t tsl, void* arg);
 
-void tsdb_sample(fdb_tsdb_t tsdb)
-{
+void tsdb_sample(fdb_tsdb_t tsdb) {
     struct fdb_blob blob;
 
     FDB_INFO("==================== tsdb_sample ====================\n");
@@ -48,12 +47,14 @@ void tsdb_sample(fdb_tsdb_t tsdb)
         status.temp = 36;
         status.humi = 85;
         fdb_tsl_append(tsdb, fdb_blob_make(&blob, &status, sizeof(status)));
-        FDB_INFO("append the new status.temp (%d) and status.humi (%d)\n", status.temp, status.humi);
+        FDB_INFO("append the new status.temp (%d) and status.humi (%d)\n",
+                 status.temp, status.humi);
 
         status.temp = 38;
         status.humi = 90;
         fdb_tsl_append(tsdb, fdb_blob_make(&blob, &status, sizeof(status)));
-        FDB_INFO("append the new status.temp (%d) and status.humi (%d)\n", status.temp, status.humi);
+        FDB_INFO("append the new status.temp (%d) and status.humi (%d)\n",
+                 status.temp, status.humi);
     }
 
     { /* QUERY the TSDB */
@@ -62,9 +63,20 @@ void tsdb_sample(fdb_tsdb_t tsdb)
     }
 
     { /* QUERY the TSDB by time */
-        /* prepare query time (from 1970-01-01 00:00:00 to 2020-05-05 00:00:00) */
-        struct tm tm_from = { .tm_year = 1970 - 1900, .tm_mon = 0, .tm_mday = 1, .tm_hour = 0, .tm_min = 0, .tm_sec = 0 };
-        struct tm tm_to = { .tm_year = 2020 - 1900, .tm_mon = 4, .tm_mday = 5, .tm_hour = 0, .tm_min = 0, .tm_sec = 0 };
+        /* prepare query time (from 1970-01-01 00:00:00 to 2020-05-05 00:00:00)
+         */
+        struct tm tm_from = {.tm_year = 1970 - 1900,
+                             .tm_mon = 0,
+                             .tm_mday = 1,
+                             .tm_hour = 0,
+                             .tm_min = 0,
+                             .tm_sec = 0};
+        struct tm tm_to = {.tm_year = 2020 - 1900,
+                           .tm_mon = 4,
+                           .tm_mday = 5,
+                           .tm_hour = 0,
+                           .tm_min = 0,
+                           .tm_sec = 0};
         time_t from_time = mktime(&tm_from), to_time = mktime(&tm_to);
         size_t count;
         /* query all TSL in TSDB by time */
@@ -79,8 +91,8 @@ void tsdb_sample(fdb_tsdb_t tsdb)
          * set_status_cb: the change operation will in this callback
          *
          * NOTE: The actions to modify the state must be in order.
-         *       like: FDB_TSL_WRITE -> FDB_TSL_USER_STATUS1 -> FDB_TSL_DELETED -> FDB_TSL_USER_STATUS2
-         *       The intermediate states can also be ignored.
+         *       like: FDB_TSL_WRITE -> FDB_TSL_USER_STATUS1 -> FDB_TSL_DELETED
+         * -> FDB_TSL_USER_STATUS2 The intermediate states can also be ignored.
          *       such as: FDB_TSL_WRITE -> FDB_TSL_DELETED
          */
         fdb_tsl_iter(tsdb, set_status_cb, tsdb);
@@ -89,38 +101,43 @@ void tsdb_sample(fdb_tsdb_t tsdb)
     FDB_INFO("===========================================================\n");
 }
 
-static bool query_cb(fdb_tsl_t tsl, void *arg)
-{
+static bool query_cb(fdb_tsl_t tsl, void* arg) {
     struct fdb_blob blob;
     // rbg/kcf
-    struct env_status status={0};
+    struct env_status status = {0};
     fdb_tsdb_t db = arg;
 
-    fdb_blob_read((fdb_db_t) db, fdb_tsl_to_blob(tsl, fdb_blob_make(&blob, &status, sizeof(status))));
-    FDB_INFO("[query_cb] queried a TSL: time: %ld, temp: %d, humi: %d\n", tsl->time, status.temp, status.humi);
+    fdb_blob_read(
+        (fdb_db_t)db,
+        fdb_tsl_to_blob(tsl, fdb_blob_make(&blob, &status, sizeof(status))));
+    FDB_INFO("[query_cb] queried a TSL: time: %ld, temp: %d, humi: %d\n",
+             tsl->time, status.temp, status.humi);
 
     return false;
 }
 
-static bool query_by_time_cb(fdb_tsl_t tsl, void *arg)
-{
+static bool query_by_time_cb(fdb_tsl_t tsl, void* arg) {
     struct fdb_blob blob;
-    // rbg/kcf 
-    struct env_status status={0};
+    // rbg/kcf
+    struct env_status status = {0};
     fdb_tsdb_t db = arg;
 
-    fdb_blob_read((fdb_db_t) db, fdb_tsl_to_blob(tsl, fdb_blob_make(&blob, &status, sizeof(status))));
+    fdb_blob_read(
+        (fdb_db_t)db,
+        fdb_tsl_to_blob(tsl, fdb_blob_make(&blob, &status, sizeof(status))));
     // rbg/kcf
-    FDB_INFO("[query_by_time_cb] queried a TSL: time: %ld, temp: %d, humi: %d\n", tsl->time, status.temp, status.humi);
+    FDB_INFO(
+        "[query_by_time_cb] queried a TSL: time: %ld, temp: %d, humi: %d\n",
+        tsl->time, status.temp, status.humi);
 
     return false;
 }
 
-static bool set_status_cb(fdb_tsl_t tsl, void *arg)
-{
+static bool set_status_cb(fdb_tsl_t tsl, void* arg) {
     fdb_tsdb_t db = arg;
 
-    FDB_INFO("set the TSL (time %ld) status from %d to %d\n", tsl->time, tsl->status, FDB_TSL_USER_STATUS1);
+    FDB_INFO("set the TSL (time %ld) status from %d to %d\n", tsl->time,
+             tsl->status, FDB_TSL_USER_STATUS1);
     fdb_tsl_set_status(db, tsl, FDB_TSL_USER_STATUS1);
 
     return false;

+ 2 - 1
port/linux/package/pikascript/pikascript-lib/mqtt/mqttclient.c

@@ -1021,7 +1021,8 @@ static void mqtt_yield_thread(void* arg) {
                    __FILE__, __LINE__, __FUNCTION__);
 
         // TODO: panic on ESP32S3 & FREE-RTOS
-        // pika_platform_thread_stop(c->mqtt_thread); /* mqtt is not connected to the server, stop thread */
+        // pika_platform_thread_stop(c->mqtt_thread); /* mqtt is not connected
+        // to the server, stop thread */
     }
 
     while (1) {

+ 5 - 0
port/linux/package/pikascript/void.pyi

@@ -0,0 +1,5 @@
+def void_test(a: int, b: int):
+    pass
+
+class VoidTest:
+    pass

+ 152 - 0
port/linux/test/module-test.cpp

@@ -634,6 +634,158 @@ TEST_RUN_SINGLE_FILE_PASS(struct, pack, "test/python/struct/pack.py")
 TEST_RUN_SINGLE_FILE_PASS(struct, unpack, "test/python/struct/unpack.py")
 #endif
 
+extern "C" {
+#define PIKA_USING_FLASHDB 1
+#if PIKA_USING_FLASHDB
+//#include <pthread.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include "flashdb.h"
+#define FDB_LOG_TAG "[main]"
+// static pthread_mutex_t kv_locker, ts_locker;
+static uint32_t boot_count = 0;
+static time_t boot_time[10] = {0, 1, 2, 3};
+/* default KV nodes */
+static struct fdb_default_kv_node default_kv_table[] = {
+    {"username", (void*)"armink", 0},                /* string KV */
+    {"password", (void*)"123456", 0},                /* string KV */
+    {"boot_count", &boot_count, sizeof(boot_count)}, /* int type KV */
+    {"boot_time", &boot_time, sizeof(boot_time)},    /* int array type KV */
+};
+/* KVDB object */
+static struct fdb_kvdb kvdb = {0};
+/* TSDB object */
+struct fdb_tsdb tsdb = {0};
+/* counts for simulated timestamp */
+static int counts = 0;
+
+extern void kvdb_basic_sample(fdb_kvdb_t kvdb);
+extern void kvdb_type_string_sample(fdb_kvdb_t kvdb);
+extern void kvdb_type_blob_sample(fdb_kvdb_t kvdb);
+extern void tsdb_sample(fdb_tsdb_t tsdb);
+
+/*
+static void lock(fdb_db_t db)
+{
+    pthread_mutex_lock((pthread_mutex_t *)db->user_data);
+}
+
+static void unlock(fdb_db_t db)
+{
+    pthread_mutex_unlock((pthread_mutex_t *)db->user_data);
+}
+*/
+static fdb_time_t get_time(void) {
+    return time(NULL);
+}
+#endif
+
+int test_flashdb(void) {
+#if PIKA_USING_FLASHDB
+    __platform_printf(" FDB_LOG_TAG :%s \r\n", FDB_LOG_TAG);
+    fdb_err_t result;
+    bool file_mode = true;
+    uint32_t sec_size = 4096, db_size = sec_size * 4;
+#undef FDB_USING_KVDB
+#define FDB_USING_KVDB 1
+#ifdef FDB_USING_KVDB
+    { /* KVDB Sample */
+        struct fdb_default_kv default_kv;
+
+        default_kv.kvs = default_kv_table;
+        default_kv.num = sizeof(default_kv_table) / sizeof(default_kv_table[0]);
+        /* set the lock and unlock function if you want */
+        //      pthread_mutex_init(&kv_locker, NULL);
+        //      fdb_kvdb_control(&kvdb, FDB_KVDB_CTRL_SET_LOCK, (void *)lock);
+        //      fdb_kvdb_control(&kvdb, FDB_KVDB_CTRL_SET_UNLOCK, (void
+        //      *)unlock);
+        /* set the sector and database max size */
+        fdb_kvdb_control(&kvdb, FDB_KVDB_CTRL_SET_SEC_SIZE, &sec_size);
+        fdb_kvdb_control(&kvdb, FDB_KVDB_CTRL_SET_MAX_SIZE, &db_size);
+        /* enable file mode */
+        fdb_kvdb_control(&kvdb, FDB_KVDB_CTRL_SET_FILE_MODE, &file_mode);
+        /* create database directory */
+        mkdir("test/out/fdb_kvdb1", 0777);
+        /* Key-Value database initialization
+         *
+         *       &kvdb: database object
+         *       "env": database name
+         * "test/out/fdb_kvdb1": The flash partition name base on FAL. Please
+         * make sure it's in FAL partition table. Please change to YOUR
+         * partition name. &default_kv: The default KV nodes. It will auto add
+         * to KVDB when first initialize successfully. &kv_locker: The locker
+         * object.
+         */
+        // result = fdb_kvdb_init(&kvdb, "env", "test/out/fdb_kvdb1",
+        // &default_kv, &kv_locker);
+        result = fdb_kvdb_init(&kvdb, "env", "test/out/fdb_kvdb1", &default_kv,
+                               NULL);
+
+        if (result != FDB_NO_ERR) {
+            return -1;
+        }
+
+        /* run basic KV samples */
+        kvdb_basic_sample(&kvdb);
+        /* run string KV samples */
+        kvdb_type_string_sample(&kvdb);
+        /* run blob KV samples */
+        kvdb_type_blob_sample(&kvdb);
+    }
+#endif /* FDB_USING_KVDB */
+
+//#define FDB_USING_TSDB 1
+#ifdef FDB_USING_TSDB
+    { /* TSDB Sample */
+        /* set the lock and unlock function if you want */
+        //   pthread_mutex_init(&ts_locker, NULL);
+        //    fdb_tsdb_control(&tsdb, FDB_TSDB_CTRL_SET_LOCK, (void *)lock);
+        //  fdb_tsdb_control(&tsdb, FDB_TSDB_CTRL_SET_UNLOCK, (void *)unlock);
+        /* set the sector and database max size */
+        fdb_tsdb_control(&tsdb, FDB_TSDB_CTRL_SET_SEC_SIZE, &sec_size);
+        fdb_tsdb_control(&tsdb, FDB_TSDB_CTRL_SET_MAX_SIZE, &db_size);
+        /* enable file mode */
+        fdb_tsdb_control(&tsdb, FDB_TSDB_CTRL_SET_FILE_MODE, &file_mode);
+        /* create database directory */
+        mkdir("test/out/fdb_tsdb1", 0777);
+        /* Time series database initialization
+         *
+         *       &tsdb: database object
+         *       "log": database name
+         * "test/out/fdb_tsdb1": The flash partition name base on FAL. Please
+         * make sure it's in FAL partition table. Please change to YOUR
+         * partition name. get_time: The get current timestamp function. 128:
+         * maximum length of each log ts_locker: The locker object.
+         */
+        // result = fdb_tsdb_init(&tsdb, "log", "test/out/fdb_tsdb1", get_time,
+        // 128, &ts_locker);
+        result = fdb_tsdb_init(&tsdb, "log", "test/out/fdb_tsdb1", get_time,
+                               128, NULL);
+
+        /* read last saved time for simulated timestamp */
+        fdb_tsdb_control(&tsdb, FDB_TSDB_CTRL_GET_LAST_TIME, &counts);
+
+        if (result != FDB_NO_ERR) {
+            return -1;
+        }
+
+        /* run TSDB sample */
+        tsdb_sample(&tsdb);
+    }
+#endif /* FDB_USING_TSDB */
+#else
+    __platform_printf(" FDB NOT INUSE \r\n");
+#endif
+    return 0;
+}
+}
+
+TEST(flashdb, base) {
+    EXPECT_EQ(test_flashdb(), 0);
+}
+
+TEST_RUN_SINGLE_FILE(flashdb, utest, "test/python/flashdb/flashdb_utest.py")
+
 #endif
 
 TEST_END

+ 3 - 3
port/linux/test/mqtt-test.cpp

@@ -100,7 +100,7 @@ TEST(mqtt, init) {
 #define USING_MQTT_TEST 1
 
 #if USING_MQTT_TEST
-//! Mqtt connect will break the gichub actions, 
+//! Mqtt connect will break the gichub actions,
 //! if need to test, please enable it manually.
 TEST(mqtt, connect) {
     PikaObj* pikaMain = newRootObj("pikaMain", New_PikaMain);
@@ -113,7 +113,7 @@ TEST(mqtt, connect) {
 #endif
 
 #if USING_MQTT_TEST
-//! Mqtt set_para will break the gichub actions, 
+//! Mqtt set_para will break the gichub actions,
 //! if need to test, please enable it manually.
 TEST(mqtt, set_para) {
     PikaObj* pikaMain = newRootObj("pikaMain", New_PikaMain);
@@ -126,7 +126,7 @@ TEST(mqtt, set_para) {
 #endif
 
 #if USING_MQTT_TEST
-//! Mqtt publish will break the gichub actions, 
+//! Mqtt publish will break the gichub actions,
 //! if need to test, please enable it manually.
 TEST(mqtt, publish) {
     PikaObj* pikaMain = newRootObj("pikaMain", New_PikaMain);

+ 88 - 0
port/linux/test/python/flashdb/flashdb_utest.py

@@ -0,0 +1,88 @@
+import flashdb
+import unittest
+import struct
+
+kvdb=None
+fdb=flashdb.FDB()
+
+class FlashDBUnitTest(unittest.TestCase):
+    def test_boot_count1(self):
+        print('test boot_count increment 1')
+        if fdb is None:
+            fdb = flashdb.FDB()
+        boot_count=0
+        boot_count_blob = struct.pack('i', boot_count)
+        boot_times = [0,1,2,3,0,0,0,0,0,0]
+        boot_time_tuple = tuple(boot_times)
+        boot_time_blob  = struct.pack('@10Q', *boot_time_tuple)
+        default_kv={
+            'username': 'armink',  # string KV
+            'password': "123456",   # string KV
+            'boot_count': boot_count_blob,       # int type kv
+            'boot_time': boot_time_blob,   # array type kv
+        }
+        #print(default_kv)
+        kvdb = fdb.kvdb_init("env", "fdb_kvdb", default_kv, None)
+
+        res = fdb.kv_get_blob(kvdb, "boot_count", len(boot_count_blob))
+        self.assertIsNotNone(res)
+        
+        boot_count = struct.unpack("i", res)[0]      
+        boot_count= boot_count+1
+        boot_count_blob = struct.pack('i', boot_count)
+        
+        fdb.kv_set_blob(kvdb, "boot_count", boot_count_blob)
+        res = fdb.kv_get_blob(kvdb, "boot_count", len(boot_count_blob))
+        self.assertIsNotNone(res)
+
+        new_boot_count = struct.unpack("i", res)[0]      
+        
+        self.assertEqual(new_boot_count, boot_count)
+        
+    def test_boot_count2(self):
+        print('test boot_count increment 2')
+        if fdb is None:
+            fdb = flashdb.FDB()
+        boot_count=0
+        boot_count_fmt='i'
+        boot_count_blob = struct.pack(boot_count_fmt, boot_count)
+        boot_count_size = len(boot_count_blob)
+
+        boot_time = [0,1,2,3,0,0,0,0,0,0]
+        boot_time_fmt='@10Q'
+        boot_time_tuple = tuple(boot_time)
+        boot_time_blob  = struct.pack(boot_time_fmt, *boot_time_tuple)
+
+        default_kv={
+            'username': 'armink',  # string KV
+            'password': "123456",   # string KV
+            'boot_count': boot_count_blob,       # int type kv
+            'boot_time': boot_time_blob,   # array type kv
+        }
+        #print(default_kv)
+        kvdb = fdb.kvdb_init("env", "fdb_kvdb", default_kv, None)
+
+        boot_count = fdb.kv_get_by_fmt(kvdb, "boot_count", boot_count_size, boot_count_fmt)
+        self.assertIsNotNone(boot_count)
+        print("==================== kvdb_basic_sample ====================")
+        print( "get the 'boot_count' value is %d" % boot_count)
+        
+        boot_count = boot_count +1
+        res =fdb.kv_set_by_fmt(kvdb, "boot_count", boot_count, boot_count_fmt)
+
+        new_boot_count = fdb.kv_get_by_fmt(kvdb, "boot_count", boot_count_size, boot_count_fmt)
+        self.assertIsNotNone(new_boot_count)
+        print( "get the 'boot_count' value is %d" % new_boot_count)
+        print("===========================================================")
+
+        self.assertEqual(new_boot_count, boot_count)
+        
+
+
+def utest():
+    suit = unittest.TestSuite("test1")
+    suit.addTest(FlashDBUnitTest())
+    runner = unittest.TextTestRunner()
+    res = runner.run(suit)
+    
+utest() 

+ 1 - 1
src/PikaVersion.h

@@ -2,4 +2,4 @@
 #define PIKA_VERSION_MINOR 12
 #define PIKA_VERSION_MICRO 6
 
-#define PIKA_EDIT_TIME "2023/10/04 22:51:03"
+#define PIKA_EDIT_TIME "2023/10/09 21:25:14"

Niektoré súbory nie sú zobrazené, pretože je v týchto rozdielových dátach zmenené mnoho súborov