luat_lib_fskv.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545
  1. /*
  2. @module fskv
  3. @summary kv数据库,掉电不丢数据
  4. @version 1.0
  5. @date 2022.12.29
  6. @demo fskv
  7. @tag LUAT_USE_FSKV
  8. @usage
  9. -- 本库的目标是替代fdb库
  10. -- 1. 兼容fdb的函数
  11. -- 2. 使用fdb的flash空间,启用时也会替代fdb库
  12. -- 3. 功能上与EEPROM是类似的
  13. fskv.init()
  14. fskv.set("wendal", 1234)
  15. log.info("fskv", "wendal", fskv.get("wendal"))
  16. --[[
  17. fskv与fdb的实现机制导致的差异
  18. fskv fdb
  19. 1. value长度 4096 255
  20. 2. key长度 63 64
  21. 3. 空间利用率(对比) 较低 较高
  22. 4. 读取速度 恒定 脏数据影响速度,非恒定
  23. 5. 写入数据 恒定 脏数据影响速度,非恒定
  24. 6. 均衡擦除 自动 自动
  25. ]]
  26. */
  27. #include "luat_base.h"
  28. #include "luat_msgbus.h"
  29. #include "luat_mem.h"
  30. #include "luat_fskv.h"
  31. #include "luat_sfd.h"
  32. #ifndef LUAT_LOG_TAG
  33. #define LUAT_LOG_TAG "fskv"
  34. #include "luat_log.h"
  35. #endif
  36. #define LUAT_FSKV_MAX_SIZE (4096)
  37. #ifndef LUAT_CONF_FSKV_CUSTOM
  38. extern sfd_drv_t* sfd_onchip;
  39. extern luat_sfd_lfs_t* sfd_lfs;
  40. #endif
  41. static int fskv_inited;
  42. // static char fskv_read_buff[LUAT_FSKV_MAX_SIZE];
  43. /**
  44. 初始化kv数据库
  45. @api fskv.init()
  46. @return boolean 成功返回true,否则返回false
  47. @usage
  48. if fskv.init() then
  49. log.info("fdb", "kv数据库初始化成功")
  50. end
  51. -- 关于清空fdb库
  52. -- 下载工具是没有提供直接清除fdb数据的途径的, 但有办法解决
  53. -- 写一个main.lua, 执行 fskv.kvdb_init 后 执行 fskv.clear() 即可全清fdb数据.
  54. */
  55. static int l_fskvdb_init(lua_State *L) {
  56. if (fskv_inited == 0) {
  57. #ifndef LUAT_CONF_FSKV_CUSTOM
  58. if (sfd_onchip == NULL) {
  59. luat_sfd_onchip_init();
  60. }
  61. if (sfd_onchip == NULL) {
  62. LLOGE("sfd-onchip init failed");
  63. return 0;
  64. }
  65. if (sfd_lfs == NULL) {
  66. luat_sfd_lfs_init(sfd_onchip);
  67. }
  68. if (sfd_lfs == NULL) {
  69. LLOGE("sfd-onchip lfs int failed");
  70. return 0;
  71. }
  72. fskv_inited = 1;
  73. #else
  74. fskv_inited = luat_fskv_init() == 0;
  75. #endif
  76. }
  77. lua_pushboolean(L, fskv_inited);
  78. return 1;
  79. }
  80. /**
  81. 设置一对kv数据
  82. @api fskv.set(key, value)
  83. @string key的名称,必填,不能空字符串
  84. @string 用户数据,必填,不能nil, 支持字符串/数值/table/布尔值, 数据长度最大4095字节
  85. @return boolean 成功返回true,否则返回false
  86. @usage
  87. -- 设置数据, 字符串,数值,table,布尔值,均可
  88. -- 但不可以是nil, function, userdata, task
  89. log.info("fdb", fskv.set("wendal", "goodgoodstudy"))
  90. log.info("fdb", fskv.set("upgrade", true))
  91. log.info("fdb", fskv.set("timer", 1))
  92. log.info("fdb", fskv.set("bigd", {name="wendal",age=123}))
  93. */
  94. static int l_fskv_set(lua_State *L) {
  95. if (fskv_inited == 0) {
  96. LLOGE("call fskv.init() first!!!");
  97. return 0;
  98. }
  99. size_t len;
  100. luaL_Buffer buff;
  101. luaL_buffinit(L, &buff);
  102. const char* key = luaL_checkstring(L, 1);
  103. //luaL_addchar(&buff, 0xA5);
  104. int type = lua_type(L, 2);
  105. switch (type)
  106. {
  107. case LUA_TBOOLEAN:
  108. luaL_addchar(&buff, LUA_TBOOLEAN);
  109. bool val = lua_toboolean(L, 2);
  110. luaL_addlstring(&buff, (const char*)&val, sizeof(val));
  111. break;
  112. case LUA_TNUMBER:
  113. if (lua_isinteger(L, 2)) {
  114. luaL_addchar(&buff, LUA_TINTEGER); // 自定义类型
  115. lua_Integer val = luaL_checkinteger(L, 2);
  116. luaL_addlstring(&buff, (const char*)&val, sizeof(val));
  117. }
  118. else {
  119. luaL_addchar(&buff, LUA_TNUMBER);
  120. lua_getglobal(L, "pack");
  121. if (lua_isnil(L, -1)) {
  122. LLOGW("float number need pack lib");
  123. lua_pushboolean(L, 0);
  124. return 1;
  125. }
  126. lua_getfield(L, -1, "pack");
  127. lua_pushstring(L, ">f");
  128. lua_pushvalue(L, 2);
  129. lua_call(L, 2, 1);
  130. if (lua_isstring(L, -1)) {
  131. const char* val = luaL_checklstring(L, -1, &len);
  132. luaL_addlstring(&buff, val, len);
  133. }
  134. else {
  135. LLOGW("kdb store number fail!!");
  136. lua_pushboolean(L, 0);
  137. return 1;
  138. }
  139. }
  140. break;
  141. case LUA_TSTRING:
  142. {
  143. luaL_addchar(&buff, LUA_TSTRING);
  144. const char* val = luaL_checklstring(L, 2, &len);
  145. luaL_addlstring(&buff, val, len);
  146. break;
  147. }
  148. case LUA_TTABLE:
  149. {
  150. lua_settop(L, 2);
  151. lua_getglobal(L, "json");
  152. if (lua_isnil(L, -1)) {
  153. LLOGW("miss json lib, not support table value");
  154. lua_pushboolean(L, 0);
  155. return 1;
  156. }
  157. lua_getfield(L, -1, "encode");
  158. if (lua_isfunction(L, -1)) {
  159. lua_pushvalue(L, 2);
  160. lua_call(L, 1, 1);
  161. if (lua_isstring(L, -1)) {
  162. luaL_addchar(&buff, LUA_TTABLE);
  163. const char* val = luaL_checklstring(L, -1, &len);
  164. luaL_addlstring(&buff, val, len);
  165. }
  166. else {
  167. LLOGW("json.encode(val) report error");
  168. lua_pushboolean(L, 0);
  169. return 1;
  170. }
  171. }
  172. else {
  173. LLOGW("miss json.encode, not support table value");
  174. lua_pushboolean(L, 0);
  175. return 1;
  176. }
  177. break;
  178. }
  179. default:
  180. {
  181. LLOGW("function/userdata/nil/thread isn't allow");
  182. lua_pushboolean(L, 0);
  183. return 1;
  184. }
  185. }
  186. if (buff.n > LUAT_FSKV_MAX_SIZE) {
  187. LLOGE("value too big %d max %d", buff.n, LUAT_FSKV_MAX_SIZE);
  188. lua_pushboolean(L, 0);
  189. return 1;
  190. }
  191. int ret = luat_fskv_set(key, buff.b, buff.n);
  192. lua_pushboolean(L, ret == buff.n ? 1 : 0);
  193. // lua_pushinteger(L, ret);
  194. return 1;
  195. }
  196. /**
  197. 设置table内的键值对数据
  198. @api fskv.sett(key, skey, value)
  199. @string key的名称,必填,不能空字符串
  200. @string table的key名称, 必填, 不能是空字符串
  201. @string 用户数据,必填,支持字符串/数值/table/布尔值, 数据长度最大4095字节
  202. @return boolean 成功返回true,否则返回false/nil
  203. @usage
  204. -- 本API在2023.7.26新增,注意与set函数区别
  205. -- 设置数据, 字符串,数值,table,布尔值,均可
  206. -- 但不可以是function, userdata, task
  207. log.info("fdb", fskv.sett("mytable", "wendal", "goodgoodstudy"))
  208. log.info("fdb", fskv.sett("mytable", "upgrade", true))
  209. log.info("fdb", fskv.sett("mytable", "timer", 1))
  210. log.info("fdb", fskv.sett("mytable", "bigd", {name="wendal",age=123}))
  211. -- 下列语句将打印出4个元素的table
  212. log.info("fdb", fskv.get("mytable"), json.encode(fskv.get("mytable")))
  213. -- 注意: 如果key不存在, 或者原本的值不是table类型,将会完全覆盖
  214. -- 例如下列写法,最终获取到的是table,而非第一行的字符串
  215. log.info("fdb", fskv.set("mykv", "123"))
  216. log.info("fdb", fskv.sett("mykv", "age", "123")) -- 保存的将是 {age:"123"}
  217. -- 如果设置的数据填nil, 代表删除对应的key
  218. log.info("fdb", fskv.sett("mykv", "name", "wendal"))
  219. log.info("fdb", fskv.sett("mykv", "name")) -- 相当于删除
  220. --
  221. */
  222. static int l_fskv_sett(lua_State *L) {
  223. if (fskv_inited == 0) {
  224. LLOGE("call fskv.init() first!!!");
  225. return 0;
  226. }
  227. const char* key = luaL_checkstring(L, 1);
  228. const char* skey = luaL_checkstring(L, 2);
  229. if (lua_gettop(L) < 3) {
  230. LLOGD("require key skey value");
  231. return 0;
  232. }
  233. char tmp[256] = {0};
  234. char *buff = NULL;
  235. char *rbuff = NULL;
  236. int size = luat_fskv_size(key, tmp);
  237. if (size >= 256) {
  238. rbuff = luat_heap_malloc(size);
  239. if (rbuff == NULL) {
  240. LLOGW("out of memory when malloc key-value buff");
  241. return 0;
  242. }
  243. size_t read_len = luat_fskv_get(key, rbuff, size);
  244. if (read_len != size) {
  245. luat_heap_free(rbuff);
  246. LLOGW("read key-value fail, ignore as not exist");
  247. return 0;
  248. }
  249. buff = rbuff;
  250. }
  251. else {
  252. buff = tmp;
  253. }
  254. if (buff[0] == LUA_TTABLE) {
  255. lua_getglobal(L, "json");
  256. lua_getfield(L, -1, "decode");
  257. lua_pushlstring(L, (const char*)(buff + 1), size - 1);
  258. lua_call(L, 1, 1);
  259. if (lua_type(L, -1) != LUA_TTABLE) {
  260. lua_pop(L, 1);
  261. lua_newtable(L);
  262. }
  263. }
  264. else {
  265. lua_newtable(L);
  266. }
  267. if (rbuff) {
  268. luat_heap_free(rbuff);
  269. rbuff = NULL;
  270. }
  271. lua_pushvalue(L, 3);
  272. lua_setfield(L, -2, skey);
  273. lua_pushcfunction(L, l_fskv_set);
  274. lua_pushvalue(L, 1);
  275. lua_pushvalue(L, -3);
  276. lua_call(L, 2, 1);
  277. return 1;
  278. }
  279. /**
  280. 根据key获取对应的数据
  281. @api fskv.get(key, skey)
  282. @string key的名称,必填,不能空字符串
  283. @string 可选的次级key,仅当原始值为table时有效,相当于 fskv.get(key)[skey]
  284. @return any 存在则返回数据,否则返回nil
  285. @usage
  286. if fskv.init() then
  287. log.info("fdb", fskv.get("wendal"))
  288. end
  289. -- 若需要"默认值", 对应非bool布尔值, 可以这样写
  290. local v = fskv.get("wendal") or "123"
  291. */
  292. static int l_fskv_get(lua_State *L) {
  293. if (fskv_inited == 0) {
  294. LLOGE("call fskv.init() first!!!");
  295. return 0;
  296. }
  297. // luaL_Buffer buff;
  298. const char* key = luaL_checkstring(L, 1);
  299. const char* skey = luaL_optstring(L, 2, "");
  300. // luaL_buffinitsize(L, &buff, 8192);
  301. char tmp[256] = {0};
  302. char *buff = NULL;
  303. char *rbuff = NULL;
  304. int size = luat_fskv_size(key, tmp);
  305. if (size < 2) {
  306. return 0; // 对应的KEY不存在
  307. }
  308. if (size >= 256) {
  309. rbuff = luat_heap_malloc(size);
  310. if (rbuff == NULL) {
  311. LLOGW("out of memory when malloc key-value buff");
  312. return 0;
  313. }
  314. size_t read_len = luat_fskv_get(key, rbuff, size);
  315. if (read_len != size) {
  316. luat_heap_free(rbuff);
  317. LLOGW("read key-value fail, ignore as not exist");
  318. return 0;
  319. }
  320. buff = rbuff;
  321. }
  322. else {
  323. buff = tmp;
  324. }
  325. lua_Integer intVal;
  326. // lua_Number *numVal;
  327. // LLOGD("KV value T=%02X", buff.b[0]);
  328. switch(buff[0]) {
  329. case LUA_TBOOLEAN:
  330. lua_pushboolean(L, buff[1]);
  331. break;
  332. case LUA_TNUMBER:
  333. lua_getglobal(L, "pack");
  334. lua_getfield(L, -1, "unpack");
  335. lua_pushlstring(L, (char*)(buff + 1), size - 1);
  336. lua_pushstring(L, ">f");
  337. lua_call(L, 2, 2);
  338. // _, val = pack.unpack(data, ">f")
  339. break;
  340. case LUA_TINTEGER:
  341. //不能直接赋值,右边指针地址和左边的位宽不一致
  342. memcpy(&intVal, &buff[1], sizeof(lua_Integer));
  343. // intVal = (lua_Integer*)(&buff[1]);
  344. // lua_pushinteger(L, *intVal);
  345. lua_pushinteger(L, intVal);
  346. break;
  347. case LUA_TSTRING:
  348. lua_pushlstring(L, (const char*)(buff + 1), size - 1);
  349. break;
  350. case LUA_TTABLE:
  351. lua_getglobal(L, "json");
  352. lua_getfield(L, -1, "decode");
  353. lua_pushlstring(L, (const char*)(buff + 1), size - 1);
  354. lua_call(L, 1, 1);
  355. if (strlen(skey) > 0 && lua_istable(L, -1)) {
  356. lua_getfield(L, -1, skey);
  357. }
  358. break;
  359. default :
  360. LLOGW("bad value prefix %02X", buff[0]);
  361. lua_pushnil(L);
  362. break;
  363. }
  364. if (rbuff)
  365. luat_heap_free(rbuff);
  366. return 1;
  367. }
  368. /**
  369. 根据key删除数据
  370. @api fskv.del(key)
  371. @string key的名称,必填,不能空字符串
  372. @return bool 成功返回true,否则返回false
  373. @usage
  374. log.info("fdb", fskv.del("wendal"))
  375. */
  376. static int l_fskv_del(lua_State *L) {
  377. if (fskv_inited == 0) {
  378. LLOGE("call fskv.init() first!!!");
  379. return 0;
  380. }
  381. const char* key = luaL_checkstring(L, 1);
  382. if (key == NULL) {
  383. lua_pushboolean(L, 0);
  384. return 1;
  385. }
  386. int ret = luat_fskv_del(key);
  387. lua_pushboolean(L, ret == 0 ? 1 : 0);
  388. return 1;
  389. }
  390. /**
  391. 清空整个kv数据库
  392. @api fskv.clear()
  393. @return bool 成功返回true,否则返回false
  394. @usage
  395. -- 清空
  396. fskv.clear()
  397. */
  398. static int l_fskv_clr(lua_State *L) {
  399. if (fskv_inited == 0) {
  400. LLOGE("call fskv.init() first!!!");
  401. return 0;
  402. }
  403. int ret = luat_fskv_clear();
  404. lua_pushboolean(L, ret == 0 ? 1 : 0);
  405. return 1;
  406. }
  407. /**
  408. kv数据库迭代器
  409. @api fskv.iter()
  410. @return userdata 成功返回迭代器指针,否则返回nil
  411. @usage
  412. -- 清空
  413. local iter = fskv.iter()
  414. if iter then
  415. while 1 do
  416. local k = fskv.next(iter)
  417. if not k then
  418. break
  419. end
  420. log.info("fdb", k, "value", fskv.kv_get(k))
  421. end
  422. end
  423. */
  424. static int l_fskv_iter(lua_State *L) {
  425. if (fskv_inited == 0) {
  426. LLOGE("call fskv.init() first!!!");
  427. return 0;
  428. }
  429. size_t *offset = lua_newuserdata(L, sizeof(size_t));
  430. memset(offset, 0, sizeof(size_t));
  431. return 1;
  432. }
  433. /**
  434. kv迭代器获取下一个key
  435. @api fskv.next(iter)
  436. @userdata fskv.iter()返回的指针
  437. @return string 成功返回字符串key值, 否则返回nil
  438. @usage
  439. -- 清空
  440. local iter = fskv.iter()
  441. if iter then
  442. while 1 do
  443. local k = fskv.next(iter)
  444. if not k then
  445. break
  446. end
  447. log.info("fskv", k, "value", fskv.get(k))
  448. end
  449. end
  450. */
  451. static int l_fskv_next(lua_State *L) {
  452. size_t *offset = lua_touserdata(L, 1);
  453. char buff[256] = {0};
  454. int ret = luat_fskv_next(buff, *offset);
  455. // LLOGD("fskv.next %d %d", *offset, ret);
  456. if (ret == 0) {
  457. lua_pushstring(L, buff);
  458. *offset = *offset + 1;
  459. return 1;
  460. }
  461. return 0;
  462. }
  463. /*
  464. 获取kv数据库状态
  465. @api fskv.status()
  466. @return int 已使用的空间,单位字节
  467. @return int 总可用空间, 单位字节
  468. @return int 总kv键值对数量, 单位个
  469. @usage
  470. local used, total,kv_count = fskv.status()
  471. log.info("fdb", "kv", used,total,kv_count)
  472. */
  473. static int l_fskv_stat(lua_State *L) {
  474. size_t using_sz = 0;
  475. size_t max_sz = 0;
  476. size_t kv_count = 0;
  477. if (fskv_inited == 0) {
  478. LLOGE("call fskv.init() first!!!");
  479. return 0;
  480. }
  481. luat_fskv_stat(&using_sz, &max_sz, &kv_count);
  482. lua_pushinteger(L, using_sz);
  483. lua_pushinteger(L, max_sz);
  484. lua_pushinteger(L, kv_count);
  485. return 3;
  486. }
  487. #include "rotable2.h"
  488. static const rotable_Reg_t reg_fskv[] =
  489. {
  490. { "init" , ROREG_FUNC(l_fskvdb_init)},
  491. { "set", ROREG_FUNC(l_fskv_set)},
  492. { "get", ROREG_FUNC(l_fskv_get)},
  493. { "del", ROREG_FUNC(l_fskv_del)},
  494. { "clr", ROREG_FUNC(l_fskv_clr)},
  495. { "clear", ROREG_FUNC(l_fskv_clr)},
  496. { "stat", ROREG_FUNC(l_fskv_stat)},
  497. { "status", ROREG_FUNC(l_fskv_stat)},
  498. { "iter", ROREG_FUNC(l_fskv_iter)},
  499. { "next", ROREG_FUNC(l_fskv_next)},
  500. { "sett", ROREG_FUNC(l_fskv_sett)},
  501. // -- 提供与fdb兼容的API
  502. { "kvdb_init" , ROREG_FUNC(l_fskvdb_init)},
  503. { "kv_set", ROREG_FUNC(l_fskv_set)},
  504. { "kv_get", ROREG_FUNC(l_fskv_get)},
  505. { "kv_del", ROREG_FUNC(l_fskv_del)},
  506. { "kv_clr", ROREG_FUNC(l_fskv_clr)},
  507. { "kv_stat", ROREG_FUNC(l_fskv_stat)},
  508. { "kv_iter", ROREG_FUNC(l_fskv_iter)},
  509. { "kv_next", ROREG_FUNC(l_fskv_next)},
  510. { NULL, ROREG_INT(0)}
  511. };
  512. LUAMOD_API int luaopen_fskv( lua_State *L ) {
  513. luat_newlib2(L, reg_fskv);
  514. lua_pushvalue(L, -1);
  515. lua_setglobal(L, "fdb");
  516. return 1;
  517. }