/** *! * \file b_mod_kv.c * \version v0.0.2 * \date 2020/04/09 * \author Bean(notrynohigh@outlook.com) ******************************************************************************* * @attention * * Copyright (c) 2020 Bean * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO KV SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. ******************************************************************************* */ /*Includes ----------------------------------------------*/ #include "modules/inc/b_mod_kv.h" #if _KV_ENABLE #include <string.h> #include "core/inc/b_core.h" #include "drivers/inc/b_driver.h" /** * \addtogroup BABYOS * \{ */ /** * \addtogroup MODULES * \{ */ /** * \addtogroup KV * \{ */ /** * \defgroup KV_Private_TypesDefinitions * \{ */ /** * \} */ /** * \defgroup KV_Private_Defines * \{ */ /** * \} */ /** * \defgroup KV_Private_Macros * \{ */ /** * \} */ /** * \defgroup KV_Private_Variables * \{ */ static bKV_Info_t bKV_Info; const static bKV_Index_t InvalidIndex = {0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}; /** * \} */ /** * \defgroup KV_Private_FunctionPrototypes * \{ */ /** * \} */ /** * \defgroup KV_Private_Functions * \{ */ static uint32_t _bKV_GenerateID(const char *key) { uint32_t id = 0; uint16_t klen = strlen(key); while (klen) { id = (id << 5) + id + *key++; klen -= 1; } return id; } static int _bKV_AllocateSpace(uint32_t size, uint32_t s_addr) { uint32_t size_d2 = size / 5; uint32_t num = (size_d2 - strlen(bKV_HEAD_STR)) / sizeof(bKV_Index_t); if (num > 200) { num = 200; } if (num < 3) { b_log_e("please allocate more space\r\n"); return -1; } bKV_Info.t_max = num; bKV_Info.ts_address = s_addr + strlen(bKV_HEAD_STR); bKV_Info.te_address = bKV_Info.ts_address + (num * sizeof(bKV_Index_t)) - 1; bKV_Info.ds_address = bKV_Info.te_address + 1; bKV_Info.d_size = size_d2 * 2; bKV_Info.de_address = bKV_Info.ds_address + bKV_Info.d_size - 1; bKV_Info.d_index = bKV_Info.ds_address; return 0; } static int _bKV_ISFirstTime(int fd) { int retval = 0; uint8_t buf[16]; uint8_t buf2[16]; bLseek(fd, bKV_Info.str_address); retval = bRead(fd, buf, strlen(bKV_HEAD_STR)); if (bKV_Info.e_size > 0) { bLseek(fd, bKV_Info.str_address + bKV_Info.e_size); retval = bRead(fd, buf2, strlen(bKV_HEAD_STR)); } if (retval > 0) { if (strncmp(bKV_HEAD_STR, (const char *)buf, strlen(bKV_HEAD_STR)) == 0 || strncmp(bKV_HEAD_STR, (const char *)buf2, strlen(bKV_HEAD_STR)) == 0) { return -1; } } else { b_log_e("_bKV_ISNew read dev error\r\n"); return -2; } return 0; } static int _bKV_ClearSector(int fd, uint8_t t) { bFlashErase_t cmd; int retval = -1; if (bKV_Info.e_size == 0) { return 0; } if (t & bKV_SECTOR_T1) { cmd.addr = bKV_Info.ts_address; cmd.num = 1; retval = bCtl(fd, bCMD_ERASE_SECTOR, &cmd); } if (t & bKV_SECTOR_T2) { cmd.addr = bKV_Info.ts_address + bKV_Info.e_size; cmd.num = 1; retval = bCtl(fd, bCMD_ERASE_SECTOR, &cmd); } if (t & bKV_SECTOR_D1) { cmd.addr = bKV_Info.ds_address; cmd.num = bKV_Info.d_size / bKV_Info.e_size; retval = bCtl(fd, bCMD_ERASE_SECTOR, &cmd); } if (t & bKV_SECTOR_D2) { cmd.addr = bKV_Info.ds_address + bKV_Info.d_size; cmd.num = bKV_Info.d_size / bKV_Info.e_size; retval = bCtl(fd, bCMD_ERASE_SECTOR, &cmd); } return retval; } static int _bKV_AddHeadString(int fd) { int retval = -1; bLseek(fd, bKV_Info.str_address + bKV_Info.e_size * bKV_Info.index); retval = bWrite(fd, (uint8_t *)bKV_HEAD_STR, strlen(bKV_HEAD_STR)); return retval; } static int _bKV_Locate(int fd, uint8_t t) { bKV_Index_t tmp; uint16_t max_num = bKV_Info.t_max; uint32_t left = 1, right = max_num, index = max_num / 2; uint8_t buf[16]; int i = 0; uint32_t addr = 0; if (t > 1 || (bKV_Info.e_size == 0 && t != 0)) { return -1; } bLseek(fd, bKV_Info.str_address + bKV_Info.e_size * t); bRead(fd, buf, strlen(bKV_HEAD_STR)); if (strncmp(bKV_HEAD_STR, (const char *)buf, strlen(bKV_HEAD_STR))) { return -1; } while (left < right) { index = left + ((right - left) / 2); bLseek(fd, bKV_Info.ts_address + t * bKV_Info.e_size + index * sizeof(bKV_Index_t)); bRead(fd, (uint8_t *)&tmp, sizeof(bKV_Index_t)); if (tmp.address == 0xFFFFFFFF || tmp.id == 0xFFFFFFFF || tmp.len == 0xFFFFFFFF) { right = index; } else { left = index + 1; } } if (right == 0) { bKV_Info.d_index = bKV_Info.ds_address; } else { if (bKV_Info.e_size == 0) { for (i = 0; i < right; i++) { bLseek(fd, bKV_Info.ts_address + t * bKV_Info.e_size + i * sizeof(bKV_Index_t)); bRead(fd, (uint8_t *)&tmp, sizeof(bKV_Index_t)); if (addr < (tmp.address + tmp.len)) { addr = tmp.address + tmp.len; } } bKV_Info.d_index = addr; } else { bLseek(fd, bKV_Info.ts_address + t * bKV_Info.e_size + (right - 1) * sizeof(bKV_Index_t)); bRead(fd, (uint8_t *)&tmp, sizeof(bKV_Index_t)); bKV_Info.d_index = tmp.address + tmp.len; } } return right; } static int _bKV_LoadInfo(int fd) { int t0, t1; t0 = _bKV_Locate(fd, 0); t1 = _bKV_Locate(fd, 1); if (bKV_Info.e_size == 0) { if (bKV_Info.d_index > bKV_Info.de_address) { bKV_Info.index = 1; } if (t0 < 0) { return -1; } bKV_Info.t_index = t0; } else { if (t0 < 0 && t1 < 0) { return -1; } bKV_Info.t_index = t0; bKV_Info.index = 0; if (t1 > t0) { bKV_Info.index = 1; bKV_Info.t_index = t1; } } b_log("t_index:%d index:%d\r\n", bKV_Info.t_index, bKV_Info.index); return 0; } static int _bKV_ISExist(int fd, uint32_t id, bKV_Index_t *pt) { int i = 0; bKV_Index_t tmp; for (i = (bKV_Info.t_index - 1); i >= 0; i--) { bLseek(fd, bKV_Info.ts_address + bKV_Info.index * bKV_Info.e_size + i * sizeof(bKV_Index_t)); bRead(fd, (uint8_t *)&tmp, sizeof(bKV_Index_t)); if (bKV_Info.e_size > 0 && tmp.statu != 0xffffffff) { continue; } if (tmp.id == id) { memcpy(pt, &tmp, sizeof(bKV_Index_t)); return i; } } return -1; } static int _bKV_MoveData(int fd, uint32_t s_addr, uint32_t d_addr, uint16_t size) { uint8_t buf[64]; uint16_t r_len, len = size; for (;;) { bLseek(fd, s_addr); r_len = (len > 64) ? 64 : len; bRead(fd, buf, r_len); len = len - r_len; s_addr += r_len; bLseek(fd, d_addr); bWrite(fd, buf, r_len); d_addr += r_len; if (len == 0) { break; } } return 0; } static int _bKV_ArrangeSpace(int fd) { uint32_t i = 0; uint32_t addr = 0; bKV_Index_t tmp; uint32_t index_tmp = 0, t_addr; index_tmp = bKV_Info.index; bKV_Info.index = (bKV_Info.index + 1) % 2; t_addr = bKV_Info.ts_address + bKV_Info.index * bKV_Info.e_size; addr = bKV_Info.ds_address + bKV_Info.index * bKV_Info.d_size; if (bKV_Info.e_size > 0) { bLseek(fd, bKV_Info.str_address + bKV_Info.index * bKV_Info.e_size); bWrite(fd, (uint8_t *)bKV_HEAD_STR, strlen(bKV_HEAD_STR)); } for (i = 0; i < bKV_Info.t_index; i++) { bLseek(fd, bKV_Info.ts_address + i * sizeof(bKV_Index_t) + index_tmp * bKV_Info.e_size); bRead(fd, (uint8_t *)&tmp, sizeof(bKV_Index_t)); if (tmp.statu != 0xffffffff && bKV_Info.e_size > 0) { continue; } _bKV_MoveData(fd, tmp.address, addr, tmp.len); tmp.address = addr; addr += tmp.len; bLseek(fd, t_addr); bWrite(fd, (uint8_t *)&tmp, sizeof(bKV_Index_t) - sizeof(uint32_t)); t_addr += sizeof(bKV_Index_t); } bKV_Info.d_index = addr; bKV_Info.t_index = (t_addr - (bKV_Info.ts_address + bKV_Info.index * bKV_Info.e_size)) / sizeof(bKV_Index_t); if (index_tmp == 0) { _bKV_ClearSector(fd, bKV_SECTOR_T1 | bKV_SECTOR_D1); } else { _bKV_ClearSector(fd, bKV_SECTOR_T2 | bKV_SECTOR_D2); } b_log_w("t_index:%d index:%d\r\n", bKV_Info.t_index, bKV_Info.index); return 0; } static int _bKV_AddNew(int fd, uint32_t id, uint8_t *pbuf, uint16_t len) { bKV_Index_t tmp; tmp.len = bKV_ALIGN_4BYTES(len); if (bKV_Info.d_index + tmp.len > (bKV_Info.de_address + bKV_Info.index * bKV_Info.d_size) || bKV_Info.t_index >= bKV_Info.t_max) { _bKV_ArrangeSpace(fd); } if (bKV_Info.d_index + tmp.len > (bKV_Info.de_address + bKV_Info.index * bKV_Info.d_size) || bKV_Info.t_index >= bKV_Info.t_max) { return -1; } tmp.address = bKV_Info.d_index; tmp.id = id; tmp.real_len = len; bLseek(fd, bKV_Info.ts_address + bKV_Info.index * bKV_Info.e_size + bKV_Info.t_index * sizeof(bKV_Index_t)); bWrite(fd, (uint8_t *)&tmp, sizeof(bKV_Index_t) - sizeof(uint32_t)); bKV_Info.d_index += tmp.len; bKV_Info.t_index += 1; bLseek(fd, tmp.address); bWrite(fd, pbuf, tmp.len); b_log_w("n: t_index:%d index:%d\r\n", bKV_Info.t_index, bKV_Info.index); return 0; } static int _bKV_ModifyValue(int fd, uint32_t index, bKV_Index_t t, uint32_t id, uint8_t *pbuf, uint16_t len) { int retval = 0; uint16_t len_tmp = bKV_ALIGN_4BYTES(len); if (len_tmp > t.len || (bKV_Info.e_size > 0)) { if (bKV_Info.d_index + len_tmp > (bKV_Info.de_address + bKV_Info.index * bKV_Info.d_size) || (bKV_Info.t_index >= bKV_Info.t_max && bKV_Info.e_size > 0)) { _bKV_ArrangeSpace(fd); } if (bKV_Info.d_index + len_tmp > (bKV_Info.de_address + bKV_Info.index * bKV_Info.d_size) || (bKV_Info.t_index >= bKV_Info.t_max && bKV_Info.e_size > 0)) { return -1; } } if (bKV_Info.e_size == 0) { t.len = len_tmp; t.real_len = len; if (len_tmp <= t.len) { ; } else { t.address = bKV_Info.d_index; bKV_Info.d_index += t.len; } bLseek(fd, bKV_Info.ts_address + bKV_Info.index * bKV_Info.e_size + index * sizeof(bKV_Index_t)); bWrite(fd, (uint8_t *)&t, sizeof(bKV_Index_t) - sizeof(uint32_t)); bLseek(fd, t.address); bWrite(fd, pbuf, t.len); } else { t.statu = 0x12345678; bLseek(fd, bKV_Info.ts_address + bKV_Info.index * bKV_Info.e_size + index * sizeof(bKV_Index_t) + sizeof(bKV_Index_t) - sizeof(uint32_t)); bWrite(fd, (uint8_t *)&t.statu, sizeof(uint32_t)); retval = _bKV_AddNew(fd, id, pbuf, len); } b_log_w("t_index:%d index:%d\r\n", bKV_Info.t_index, bKV_Info.index); return retval; } static int _bKV_Get(int fd, bKV_Index_t t, uint8_t *pbuf) { int retval = 0; bLseek(fd, t.address); retval = bRead(fd, pbuf, t.real_len); return retval; } static int _bKV_DeleteKey(int fd, uint32_t index, bKV_Index_t t) { int retval = 0; if (bKV_Info.e_size == 0) { bLseek(fd, bKV_Info.ts_address + (bKV_Info.t_index - 1) * sizeof(bKV_Index_t)); bRead(fd, (uint8_t *)&t, sizeof(bKV_Index_t)); bLseek(fd, bKV_Info.ts_address + index * sizeof(bKV_Index_t)); bWrite(fd, (uint8_t *)&t, sizeof(bKV_Index_t)); bLseek(fd, bKV_Info.ts_address + (bKV_Info.t_index - 1) * sizeof(bKV_Index_t)); bWrite(fd, (uint8_t *)&InvalidIndex, sizeof(bKV_Index_t)); bKV_Info.t_index -= 1; } else { t.statu = 0x12345678; bLseek(fd, bKV_Info.ts_address + bKV_Info.index * bKV_Info.e_size + index * sizeof(bKV_Index_t) + sizeof(bKV_Index_t) - sizeof(uint32_t)); bWrite(fd, (uint8_t *)&t.statu, sizeof(uint32_t)); } return retval; } static void _bKV_InvalidTable(int fd) { int i = 0; for (i = 0; i < bKV_Info.t_max; i++) { bLseek(fd, bKV_Info.ts_address + i * sizeof(bKV_Index_t)); bWrite(fd, (uint8_t *)&InvalidIndex, sizeof(bKV_Index_t)); } } /** * \} */ /** * \addtogroup KV_Exported_Functions * \{ */ /** * \brief KV Initialize * \param dev_no Device number * \param s_addr Start address * \param size The amount of storage space that KV can use * \param e_size The minimum unit that performs erasure \note if it neednt to be erased, its 0 * \retval Result * \arg 0 OK * \arg -1 ERR */ int bKV_Init(int dev_no, uint32_t s_addr, uint32_t size, uint32_t e_size) { int retval = 0; int fd = -1; bKV_Info.status = bKV_ERROR; if (dev_no < 0) { b_log_e("dev_no %d error\r\n", dev_no); return -1; } bKV_Info.dev_no = dev_no; bKV_Info.index = 0; bKV_Info.e_size = e_size; bKV_Info.str_address = s_addr; bKV_Info.t_index = 0; if (e_size > 0) { if (size < 3 * e_size) { b_log_e("at least 4 times e_size\r\n"); return -1; } bKV_Info.ts_address = s_addr + strlen(bKV_HEAD_STR); bKV_Info.te_address = s_addr + e_size - 1; bKV_Info.t_max = (bKV_Info.te_address - bKV_Info.ts_address + 1) / sizeof(bKV_Index_t); bKV_Info.ds_address = s_addr + e_size + e_size; bKV_Info.d_size = ((size / e_size - 2) >> 1) * e_size; bKV_Info.de_address = bKV_Info.ds_address + bKV_Info.d_size - 1; bKV_Info.d_index = bKV_Info.ds_address; } else { if (0 > _bKV_AllocateSpace(size, s_addr)) { return -1; } } fd = bOpen(bKV_Info.dev_no, BCORE_FLAG_RW); if (fd < 0) { b_log_e("... open dev error\r\n"); return -1; } retval = _bKV_ISFirstTime(fd); if (retval == -2) { bKV_Info.status = bKV_ERROR; bClose(fd); return -1; } if (retval == 0) { bKV_Info.index = 0; if (0 == _bKV_ClearSector(fd, bKV_SECTOR_ALL)) { b_log("KV clear sector...ok\r\n"); if (0 > _bKV_AddHeadString(fd)) { bClose(fd); return -1; } } if (e_size == 0) { _bKV_InvalidTable(fd); } } else { if (_bKV_LoadInfo(fd) < 0) { b_log_e("load info error...\r\n"); bKV_Info.status = bKV_ERROR; bClose(fd); return -1; } } bClose(fd); bKV_Info.status = bKV_IDLE; b_log("k/v max num:%d index:%d\r\n", bKV_Info.t_max, bKV_Info.t_index); return 0; } int bKV_Set(const char *key, uint8_t *pvalue, uint16_t len) { uint32_t id = 0; int retval = 0, fd = -1; bKV_Index_t tmp; if (key == NULL || pvalue == NULL || bKV_Info.status != bKV_IDLE) { return -1; } fd = bOpen(bKV_Info.dev_no, BCORE_FLAG_RW); if (fd < 0) { b_log_e("... open dev error\r\n"); return -1; } bKV_Info.status = bKV_BUSY; id = _bKV_GenerateID(key); retval = _bKV_ISExist(fd, id, &tmp); if (retval < 0) { _bKV_AddNew(fd, id, pvalue, len); } else { _bKV_ModifyValue(fd, retval, tmp, id, pvalue, len); } bClose(fd); bKV_Info.status = bKV_IDLE; return retval; } int bKV_Get(const char *key, uint8_t *pvalue) { uint32_t id = 0; int retval = 0, fd = -1; bKV_Index_t tmp; if (key == NULL || pvalue == NULL || bKV_Info.status != bKV_IDLE) { return -1; } fd = bOpen(bKV_Info.dev_no, BCORE_FLAG_RW); if (fd < 0) { b_log_e("... open dev error\r\n"); return -1; } bKV_Info.status = bKV_BUSY; id = _bKV_GenerateID(key); retval = _bKV_ISExist(fd, id, &tmp); if (retval < 0) { bKV_Info.status = bKV_IDLE; bClose(fd); return -1; } retval = _bKV_Get(fd, tmp, pvalue); bClose(fd); bKV_Info.status = bKV_IDLE; return retval; } int bKV_Delete(const char *key) { uint32_t id = 0; int retval = 0, fd = -1; bKV_Index_t tmp; if (key == NULL || bKV_Info.status != bKV_IDLE) { return -1; } fd = bOpen(bKV_Info.dev_no, BCORE_FLAG_RW); if (fd < 0) { b_log_e("... open dev error\r\n"); return -1; } bKV_Info.status = bKV_BUSY; id = _bKV_GenerateID(key); retval = _bKV_ISExist(fd, id, &tmp); if (retval < 0) { bKV_Info.status = bKV_IDLE; bClose(fd); return -1; } _bKV_DeleteKey(fd, retval, tmp); bClose(fd); bKV_Info.status = bKV_IDLE; return retval; } /** * \} */ /** * \} */ /** * \} */ /** * \} */ #endif /************************ Copyright (c) 2020 Bean *****END OF FILE****/