nvs_storage.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772
  1. // Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. // http://www.apache.org/licenses/LICENSE-2.0
  7. //
  8. // Unless required by applicable law or agreed to in writing, software
  9. // distributed under the License is distributed on an "AS IS" BASIS,
  10. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. // See the License for the specific language governing permissions and
  12. // limitations under the License.
  13. #include "nvs_storage.hpp"
  14. #ifndef ESP_PLATFORM
  15. #include <map>
  16. #include <sstream>
  17. #endif
  18. namespace nvs
  19. {
  20. Storage::~Storage()
  21. {
  22. clearNamespaces();
  23. }
  24. void Storage::clearNamespaces()
  25. {
  26. mNamespaces.clearAndFreeNodes();
  27. }
  28. void Storage::populateBlobIndices(TBlobIndexList& blobIdxList)
  29. {
  30. for (auto it = mPageManager.begin(); it != mPageManager.end(); ++it) {
  31. Page& p = *it;
  32. size_t itemIndex = 0;
  33. Item item;
  34. /* If the power went off just after writing a blob index, the duplicate detection
  35. * logic in pagemanager will remove the earlier index. So we should never find a
  36. * duplicate index at this point */
  37. while (p.findItem(Page::NS_ANY, ItemType::BLOB_IDX, nullptr, itemIndex, item) == ESP_OK) {
  38. BlobIndexNode* entry = new BlobIndexNode;
  39. item.getKey(entry->key, sizeof(entry->key));
  40. entry->nsIndex = item.nsIndex;
  41. entry->chunkStart = item.blobIndex.chunkStart;
  42. entry->chunkCount = item.blobIndex.chunkCount;
  43. blobIdxList.push_back(entry);
  44. itemIndex += item.span;
  45. }
  46. }
  47. }
  48. void Storage::eraseOrphanDataBlobs(TBlobIndexList& blobIdxList)
  49. {
  50. for (auto it = mPageManager.begin(); it != mPageManager.end(); ++it) {
  51. Page& p = *it;
  52. size_t itemIndex = 0;
  53. Item item;
  54. /* Chunks with same <ns,key> and with chunkIndex in the following ranges
  55. * belong to same family.
  56. * 1) VER_0_OFFSET <= chunkIndex < VER_1_OFFSET-1 => Version0 chunks
  57. * 2) VER_1_OFFSET <= chunkIndex < VER_ANY => Version1 chunks
  58. */
  59. while (p.findItem(Page::NS_ANY, ItemType::BLOB_DATA, nullptr, itemIndex, item) == ESP_OK) {
  60. auto iter = std::find_if(blobIdxList.begin(),
  61. blobIdxList.end(),
  62. [=] (const BlobIndexNode& e) -> bool
  63. {return (strncmp(item.key, e.key, sizeof(e.key) - 1) == 0)
  64. && (item.nsIndex == e.nsIndex)
  65. && (item.chunkIndex >= static_cast<uint8_t> (e.chunkStart))
  66. && (item.chunkIndex < static_cast<uint8_t> (e.chunkStart) + e.chunkCount);});
  67. if (iter == std::end(blobIdxList)) {
  68. p.eraseItem(item.nsIndex, item.datatype, item.key, item.chunkIndex);
  69. }
  70. itemIndex += item.span;
  71. }
  72. }
  73. }
  74. esp_err_t Storage::init(uint32_t baseSector, uint32_t sectorCount)
  75. {
  76. auto err = mPageManager.load(baseSector, sectorCount);
  77. if (err != ESP_OK) {
  78. mState = StorageState::INVALID;
  79. return err;
  80. }
  81. // load namespaces list
  82. clearNamespaces();
  83. std::fill_n(mNamespaceUsage.data(), mNamespaceUsage.byteSize() / 4, 0);
  84. for (auto it = mPageManager.begin(); it != mPageManager.end(); ++it) {
  85. Page& p = *it;
  86. size_t itemIndex = 0;
  87. Item item;
  88. while (p.findItem(Page::NS_INDEX, ItemType::U8, nullptr, itemIndex, item) == ESP_OK) {
  89. NamespaceEntry* entry = new NamespaceEntry;
  90. item.getKey(entry->mName, sizeof(entry->mName));
  91. item.getValue(entry->mIndex);
  92. mNamespaces.push_back(entry);
  93. mNamespaceUsage.set(entry->mIndex, true);
  94. itemIndex += item.span;
  95. }
  96. }
  97. mNamespaceUsage.set(0, true);
  98. mNamespaceUsage.set(255, true);
  99. mState = StorageState::ACTIVE;
  100. // Populate list of multi-page index entries.
  101. TBlobIndexList blobIdxList;
  102. populateBlobIndices(blobIdxList);
  103. // Remove the entries for which there is no parent multi-page index.
  104. eraseOrphanDataBlobs(blobIdxList);
  105. // Purge the blob index list
  106. blobIdxList.clearAndFreeNodes();
  107. #ifndef ESP_PLATFORM
  108. debugCheck();
  109. #endif
  110. return ESP_OK;
  111. }
  112. bool Storage::isValid() const
  113. {
  114. return mState == StorageState::ACTIVE;
  115. }
  116. esp_err_t Storage::findItem(uint8_t nsIndex, ItemType datatype, const char* key, Page* &page, Item& item, uint8_t chunkIdx, VerOffset chunkStart)
  117. {
  118. for (auto it = std::begin(mPageManager); it != std::end(mPageManager); ++it) {
  119. size_t itemIndex = 0;
  120. auto err = it->findItem(nsIndex, datatype, key, itemIndex, item, chunkIdx, chunkStart);
  121. if (err == ESP_OK) {
  122. page = it;
  123. return ESP_OK;
  124. }
  125. }
  126. return ESP_ERR_NVS_NOT_FOUND;
  127. }
  128. esp_err_t Storage::writeMultiPageBlob(uint8_t nsIndex, const char* key, const void* data, size_t dataSize, VerOffset chunkStart)
  129. {
  130. uint8_t chunkCount = 0;
  131. TUsedPageList usedPages;
  132. size_t remainingSize = dataSize;
  133. size_t offset=0;
  134. esp_err_t err = ESP_OK;
  135. /* Check how much maximum data can be accommodated**/
  136. uint32_t max_pages = mPageManager.getPageCount() - 1;
  137. if(max_pages > (Page::CHUNK_ANY-1)/2) {
  138. max_pages = (Page::CHUNK_ANY-1)/2;
  139. }
  140. if (dataSize > max_pages * Page::CHUNK_MAX_SIZE) {
  141. return ESP_ERR_NVS_VALUE_TOO_LONG;
  142. }
  143. do {
  144. Page& page = getCurrentPage();
  145. size_t tailroom = page.getVarDataTailroom();
  146. size_t chunkSize =0;
  147. if (!chunkCount && tailroom < dataSize && tailroom < Page::CHUNK_MAX_SIZE/10) {
  148. /** This is the first chunk and tailroom is too small ***/
  149. if (page.state() != Page::PageState::FULL) {
  150. err = page.markFull();
  151. if (err != ESP_OK) {
  152. return err;
  153. }
  154. }
  155. err = mPageManager.requestNewPage();
  156. if (err != ESP_OK) {
  157. return err;
  158. } else if(getCurrentPage().getVarDataTailroom() == tailroom) {
  159. /* We got the same page or we are not improving.*/
  160. return ESP_ERR_NVS_NOT_ENOUGH_SPACE;
  161. } else {
  162. continue;
  163. }
  164. } else if (!tailroom) {
  165. err = ESP_ERR_NVS_NOT_ENOUGH_SPACE;
  166. break;
  167. }
  168. /* Split the blob into two and store the chunk of available size onto the current page */
  169. assert(tailroom!=0);
  170. chunkSize = (remainingSize > tailroom)? tailroom : remainingSize;
  171. remainingSize -= chunkSize;
  172. err = page.writeItem(nsIndex, ItemType::BLOB_DATA, key,
  173. static_cast<const uint8_t*> (data) + offset, chunkSize, static_cast<uint8_t> (chunkStart) + chunkCount);
  174. chunkCount++;
  175. assert(err != ESP_ERR_NVS_PAGE_FULL);
  176. if (err != ESP_OK) {
  177. break;
  178. } else {
  179. UsedPageNode* node = new UsedPageNode();
  180. node->mPage = &page;
  181. usedPages.push_back(node);
  182. if (remainingSize || (tailroom - chunkSize) < Page::ENTRY_SIZE) {
  183. if (page.state() != Page::PageState::FULL) {
  184. err = page.markFull();
  185. if (err != ESP_OK) {
  186. break;
  187. }
  188. }
  189. err = mPageManager.requestNewPage();
  190. if (err != ESP_OK) {
  191. break;
  192. }
  193. }
  194. }
  195. offset += chunkSize;
  196. if (!remainingSize) {
  197. /* All pages are stored. Now store the index.*/
  198. Item item;
  199. std::fill_n(item.data, sizeof(item.data), 0xff);
  200. item.blobIndex.dataSize = dataSize;
  201. item.blobIndex.chunkCount = chunkCount;
  202. item.blobIndex.chunkStart = chunkStart;
  203. err = getCurrentPage().writeItem(nsIndex, ItemType::BLOB_IDX, key, item.data, sizeof(item.data));
  204. assert(err != ESP_ERR_NVS_PAGE_FULL);
  205. break;
  206. }
  207. } while (1);
  208. if (err != ESP_OK) {
  209. /* Anything failed, then we should erase all the written chunks*/
  210. int ii=0;
  211. for (auto it = std::begin(usedPages); it != std::end(usedPages); it++) {
  212. it->mPage->eraseItem(nsIndex, ItemType::BLOB_DATA, key, ii++);
  213. }
  214. }
  215. usedPages.clearAndFreeNodes();
  216. return err;
  217. }
  218. esp_err_t Storage::writeItem(uint8_t nsIndex, ItemType datatype, const char* key, const void* data, size_t dataSize)
  219. {
  220. if (mState != StorageState::ACTIVE) {
  221. return ESP_ERR_NVS_NOT_INITIALIZED;
  222. }
  223. Page* findPage = nullptr;
  224. Item item;
  225. esp_err_t err;
  226. if (datatype == ItemType::BLOB) {
  227. err = findItem(nsIndex, ItemType::BLOB_IDX, key, findPage, item);
  228. } else {
  229. err = findItem(nsIndex, datatype, key, findPage, item);
  230. }
  231. if (err != ESP_OK && err != ESP_ERR_NVS_NOT_FOUND) {
  232. return err;
  233. }
  234. if (datatype == ItemType::BLOB) {
  235. VerOffset prevStart, nextStart;
  236. prevStart = nextStart = VerOffset::VER_0_OFFSET;
  237. if (findPage) {
  238. // Do a sanity check that the item in question is actually being modified.
  239. // If it isn't, it is cheaper to purposefully not write out new data.
  240. // since it may invoke an erasure of flash.
  241. if (cmpMultiPageBlob(nsIndex, key, data, dataSize) == ESP_OK) {
  242. return ESP_OK;
  243. }
  244. if (findPage->state() == Page::PageState::UNINITIALIZED ||
  245. findPage->state() == Page::PageState::INVALID) {
  246. ESP_ERROR_CHECK(findItem(nsIndex, datatype, key, findPage, item));
  247. }
  248. /* Get the version of the previous index with same <ns,key> */
  249. prevStart = item.blobIndex.chunkStart;
  250. assert(prevStart == VerOffset::VER_0_OFFSET || prevStart == VerOffset::VER_1_OFFSET);
  251. /* Toggle the version by changing the offset */
  252. nextStart
  253. = (prevStart == VerOffset::VER_1_OFFSET) ? VerOffset::VER_0_OFFSET : VerOffset::VER_1_OFFSET;
  254. }
  255. /* Write the blob with new version*/
  256. err = writeMultiPageBlob(nsIndex, key, data, dataSize, nextStart);
  257. if (err == ESP_ERR_NVS_PAGE_FULL) {
  258. return ESP_ERR_NVS_NOT_ENOUGH_SPACE;
  259. }
  260. if (err != ESP_OK) {
  261. return err;
  262. }
  263. if (findPage) {
  264. /* Erase the blob with earlier version*/
  265. err = eraseMultiPageBlob(nsIndex, key, prevStart);
  266. if (err == ESP_ERR_FLASH_OP_FAIL) {
  267. return ESP_ERR_NVS_REMOVE_FAILED;
  268. }
  269. if (err != ESP_OK) {
  270. return err;
  271. }
  272. findPage = nullptr;
  273. } else {
  274. /* Support for earlier versions where BLOBS were stored without index */
  275. err = findItem(nsIndex, datatype, key, findPage, item);
  276. if (err != ESP_OK && err != ESP_ERR_NVS_NOT_FOUND) {
  277. return err;
  278. }
  279. }
  280. } else {
  281. // Do a sanity check that the item in question is actually being modified.
  282. // If it isn't, it is cheaper to purposefully not write out new data.
  283. // since it may invoke an erasure of flash.
  284. if (findPage != nullptr &&
  285. findPage->cmpItem(nsIndex, datatype, key, data, dataSize) == ESP_OK) {
  286. return ESP_OK;
  287. }
  288. Page& page = getCurrentPage();
  289. err = page.writeItem(nsIndex, datatype, key, data, dataSize);
  290. if (err == ESP_ERR_NVS_PAGE_FULL) {
  291. if (page.state() != Page::PageState::FULL) {
  292. err = page.markFull();
  293. if (err != ESP_OK) {
  294. return err;
  295. }
  296. }
  297. err = mPageManager.requestNewPage();
  298. if (err != ESP_OK) {
  299. return err;
  300. }
  301. err = getCurrentPage().writeItem(nsIndex, datatype, key, data, dataSize);
  302. if (err == ESP_ERR_NVS_PAGE_FULL) {
  303. return ESP_ERR_NVS_NOT_ENOUGH_SPACE;
  304. }
  305. if (err != ESP_OK) {
  306. return err;
  307. }
  308. } else if (err != ESP_OK) {
  309. return err;
  310. }
  311. }
  312. if (findPage) {
  313. if (findPage->state() == Page::PageState::UNINITIALIZED ||
  314. findPage->state() == Page::PageState::INVALID) {
  315. ESP_ERROR_CHECK(findItem(nsIndex, datatype, key, findPage, item));
  316. }
  317. err = findPage->eraseItem(nsIndex, datatype, key);
  318. if (err == ESP_ERR_FLASH_OP_FAIL) {
  319. return ESP_ERR_NVS_REMOVE_FAILED;
  320. }
  321. if (err != ESP_OK) {
  322. return err;
  323. }
  324. }
  325. #ifndef ESP_PLATFORM
  326. debugCheck();
  327. #endif
  328. return ESP_OK;
  329. }
  330. esp_err_t Storage::createOrOpenNamespace(const char* nsName, bool canCreate, uint8_t& nsIndex)
  331. {
  332. if (mState != StorageState::ACTIVE) {
  333. return ESP_ERR_NVS_NOT_INITIALIZED;
  334. }
  335. auto it = std::find_if(mNamespaces.begin(), mNamespaces.end(), [=] (const NamespaceEntry& e) -> bool {
  336. return strncmp(nsName, e.mName, sizeof(e.mName) - 1) == 0;
  337. });
  338. if (it == std::end(mNamespaces)) {
  339. if (!canCreate) {
  340. return ESP_ERR_NVS_NOT_FOUND;
  341. }
  342. uint8_t ns;
  343. for (ns = 1; ns < 255; ++ns) {
  344. if (mNamespaceUsage.get(ns) == false) {
  345. break;
  346. }
  347. }
  348. if (ns == 255) {
  349. return ESP_ERR_NVS_NOT_ENOUGH_SPACE;
  350. }
  351. auto err = writeItem(Page::NS_INDEX, ItemType::U8, nsName, &ns, sizeof(ns));
  352. if (err != ESP_OK) {
  353. return err;
  354. }
  355. mNamespaceUsage.set(ns, true);
  356. nsIndex = ns;
  357. NamespaceEntry* entry = new NamespaceEntry;
  358. entry->mIndex = ns;
  359. strncpy(entry->mName, nsName, sizeof(entry->mName) - 1);
  360. entry->mName[sizeof(entry->mName) - 1] = 0;
  361. mNamespaces.push_back(entry);
  362. } else {
  363. nsIndex = it->mIndex;
  364. }
  365. return ESP_OK;
  366. }
  367. esp_err_t Storage::readMultiPageBlob(uint8_t nsIndex, const char* key, void* data, size_t dataSize)
  368. {
  369. Item item;
  370. Page* findPage = nullptr;
  371. /* First read the blob index */
  372. auto err = findItem(nsIndex, ItemType::BLOB_IDX, key, findPage, item);
  373. if (err != ESP_OK) {
  374. return err;
  375. }
  376. uint8_t chunkCount = item.blobIndex.chunkCount;
  377. VerOffset chunkStart = item.blobIndex.chunkStart;
  378. size_t readSize = item.blobIndex.dataSize;
  379. size_t offset = 0;
  380. assert(dataSize == readSize);
  381. /* Now read corresponding chunks */
  382. for (uint8_t chunkNum = 0; chunkNum < chunkCount; chunkNum++) {
  383. err = findItem(nsIndex, ItemType::BLOB_DATA, key, findPage, item, static_cast<uint8_t> (chunkStart) + chunkNum);
  384. if (err != ESP_OK) {
  385. if (err == ESP_ERR_NVS_NOT_FOUND) {
  386. break;
  387. }
  388. return err;
  389. }
  390. err = findPage->readItem(nsIndex, ItemType::BLOB_DATA, key, static_cast<uint8_t*>(data) + offset, item.varLength.dataSize, static_cast<uint8_t> (chunkStart) + chunkNum);
  391. if (err != ESP_OK) {
  392. return err;
  393. }
  394. assert(static_cast<uint8_t> (chunkStart) + chunkNum == item.chunkIndex);
  395. offset += item.varLength.dataSize;
  396. }
  397. if (err == ESP_OK) {
  398. assert(offset == dataSize);
  399. }
  400. if (err == ESP_ERR_NVS_NOT_FOUND) {
  401. eraseMultiPageBlob(nsIndex, key); // cleanup if a chunk is not found
  402. }
  403. return err;
  404. }
  405. esp_err_t Storage::cmpMultiPageBlob(uint8_t nsIndex, const char* key, const void* data, size_t dataSize)
  406. {
  407. Item item;
  408. Page* findPage = nullptr;
  409. /* First read the blob index */
  410. auto err = findItem(nsIndex, ItemType::BLOB_IDX, key, findPage, item);
  411. if (err != ESP_OK) {
  412. return err;
  413. }
  414. uint8_t chunkCount = item.blobIndex.chunkCount;
  415. VerOffset chunkStart = item.blobIndex.chunkStart;
  416. size_t readSize = item.blobIndex.dataSize;
  417. size_t offset = 0;
  418. if (dataSize != readSize) {
  419. return ESP_ERR_NVS_CONTENT_DIFFERS;
  420. }
  421. /* Now read corresponding chunks */
  422. for (uint8_t chunkNum = 0; chunkNum < chunkCount; chunkNum++) {
  423. err = findItem(nsIndex, ItemType::BLOB_DATA, key, findPage, item, static_cast<uint8_t> (chunkStart) + chunkNum);
  424. if (err != ESP_OK) {
  425. if (err == ESP_ERR_NVS_NOT_FOUND) {
  426. break;
  427. }
  428. return err;
  429. }
  430. err = findPage->cmpItem(nsIndex, ItemType::BLOB_DATA, key, static_cast<const uint8_t*>(data) + offset, item.varLength.dataSize, static_cast<uint8_t> (chunkStart) + chunkNum);
  431. if (err != ESP_OK) {
  432. return err;
  433. }
  434. assert(static_cast<uint8_t> (chunkStart) + chunkNum == item.chunkIndex);
  435. offset += item.varLength.dataSize;
  436. }
  437. if (err == ESP_OK) {
  438. assert(offset == dataSize);
  439. }
  440. return err;
  441. }
  442. esp_err_t Storage::readItem(uint8_t nsIndex, ItemType datatype, const char* key, void* data, size_t dataSize)
  443. {
  444. if (mState != StorageState::ACTIVE) {
  445. return ESP_ERR_NVS_NOT_INITIALIZED;
  446. }
  447. Item item;
  448. Page* findPage = nullptr;
  449. if (datatype == ItemType::BLOB) {
  450. auto err = readMultiPageBlob(nsIndex, key, data, dataSize);
  451. if (err != ESP_ERR_NVS_NOT_FOUND) {
  452. return err;
  453. } // else check if the blob is stored with earlier version format without index
  454. }
  455. auto err = findItem(nsIndex, datatype, key, findPage, item);
  456. if (err != ESP_OK) {
  457. return err;
  458. }
  459. return findPage->readItem(nsIndex, datatype, key, data, dataSize);
  460. }
  461. esp_err_t Storage::eraseMultiPageBlob(uint8_t nsIndex, const char* key, VerOffset chunkStart)
  462. {
  463. if (mState != StorageState::ACTIVE) {
  464. return ESP_ERR_NVS_NOT_INITIALIZED;
  465. }
  466. Item item;
  467. Page* findPage = nullptr;
  468. auto err = findItem(nsIndex, ItemType::BLOB_IDX, key, findPage, item, Page::CHUNK_ANY, chunkStart);
  469. if (err != ESP_OK) {
  470. return err;
  471. }
  472. /* Erase the index first and make children blobs orphan*/
  473. err = findPage->eraseItem(nsIndex, ItemType::BLOB_IDX, key, Page::CHUNK_ANY, chunkStart);
  474. if (err != ESP_OK) {
  475. return err;
  476. }
  477. uint8_t chunkCount = item.blobIndex.chunkCount;
  478. if (chunkStart == VerOffset::VER_ANY) {
  479. chunkStart = item.blobIndex.chunkStart;
  480. } else {
  481. assert(chunkStart == item.blobIndex.chunkStart);
  482. }
  483. /* Now erase corresponding chunks*/
  484. for (uint8_t chunkNum = 0; chunkNum < chunkCount; chunkNum++) {
  485. err = findItem(nsIndex, ItemType::BLOB_DATA, key, findPage, item, static_cast<uint8_t> (chunkStart) + chunkNum);
  486. if (err != ESP_OK && err != ESP_ERR_NVS_NOT_FOUND) {
  487. return err;
  488. } else if (err == ESP_ERR_NVS_NOT_FOUND) {
  489. continue; // Keep erasing other chunks
  490. }
  491. err = findPage->eraseItem(nsIndex, ItemType::BLOB_DATA, key, static_cast<uint8_t> (chunkStart) + chunkNum);
  492. if (err != ESP_OK) {
  493. return err;
  494. }
  495. }
  496. return ESP_OK;
  497. }
  498. esp_err_t Storage::eraseItem(uint8_t nsIndex, ItemType datatype, const char* key)
  499. {
  500. if (mState != StorageState::ACTIVE) {
  501. return ESP_ERR_NVS_NOT_INITIALIZED;
  502. }
  503. if (datatype == ItemType::BLOB) {
  504. return eraseMultiPageBlob(nsIndex, key);
  505. }
  506. Item item;
  507. Page* findPage = nullptr;
  508. auto err = findItem(nsIndex, datatype, key, findPage, item);
  509. if (err != ESP_OK) {
  510. return err;
  511. }
  512. if (item.datatype == ItemType::BLOB_DATA || item.datatype == ItemType::BLOB_IDX) {
  513. return eraseMultiPageBlob(nsIndex, key);
  514. }
  515. return findPage->eraseItem(nsIndex, datatype, key);
  516. }
  517. esp_err_t Storage::eraseNamespace(uint8_t nsIndex)
  518. {
  519. if (mState != StorageState::ACTIVE) {
  520. return ESP_ERR_NVS_NOT_INITIALIZED;
  521. }
  522. for (auto it = std::begin(mPageManager); it != std::end(mPageManager); ++it) {
  523. while (true) {
  524. auto err = it->eraseItem(nsIndex, ItemType::ANY, nullptr);
  525. if (err == ESP_ERR_NVS_NOT_FOUND) {
  526. break;
  527. }
  528. else if (err != ESP_OK) {
  529. return err;
  530. }
  531. }
  532. }
  533. return ESP_OK;
  534. }
  535. esp_err_t Storage::getItemDataSize(uint8_t nsIndex, ItemType datatype, const char* key, size_t& dataSize)
  536. {
  537. if (mState != StorageState::ACTIVE) {
  538. return ESP_ERR_NVS_NOT_INITIALIZED;
  539. }
  540. Item item;
  541. Page* findPage = nullptr;
  542. auto err = findItem(nsIndex, datatype, key, findPage, item);
  543. if (err != ESP_OK) {
  544. if (datatype != ItemType::BLOB) {
  545. return err;
  546. }
  547. err = findItem(nsIndex, ItemType::BLOB_IDX, key, findPage, item);
  548. if (err != ESP_OK) {
  549. return err;
  550. }
  551. dataSize = item.blobIndex.dataSize;
  552. return ESP_OK;
  553. }
  554. dataSize = item.varLength.dataSize;
  555. return ESP_OK;
  556. }
  557. void Storage::debugDump()
  558. {
  559. for (auto p = mPageManager.begin(); p != mPageManager.end(); ++p) {
  560. p->debugDump();
  561. }
  562. }
  563. #ifndef ESP_PLATFORM
  564. void Storage::debugCheck()
  565. {
  566. std::map<std::string, Page*> keys;
  567. for (auto p = mPageManager.begin(); p != mPageManager.end(); ++p) {
  568. size_t itemIndex = 0;
  569. size_t usedCount = 0;
  570. Item item;
  571. while (p->findItem(Page::NS_ANY, ItemType::ANY, nullptr, itemIndex, item) == ESP_OK) {
  572. std::stringstream keyrepr;
  573. keyrepr << static_cast<unsigned>(item.nsIndex) << "_" << static_cast<unsigned>(item.datatype) << "_" << item.key <<"_"<<static_cast<unsigned>(item.chunkIndex);
  574. std::string keystr = keyrepr.str();
  575. if (keys.find(keystr) != std::end(keys)) {
  576. printf("Duplicate key: %s\n", keystr.c_str());
  577. debugDump();
  578. assert(0);
  579. }
  580. keys.insert(std::make_pair(keystr, static_cast<Page*>(p)));
  581. itemIndex += item.span;
  582. usedCount += item.span;
  583. }
  584. assert(usedCount == p->getUsedEntryCount());
  585. }
  586. }
  587. #endif //ESP_PLATFORM
  588. esp_err_t Storage::fillStats(nvs_stats_t& nvsStats)
  589. {
  590. nvsStats.namespace_count = mNamespaces.size();
  591. return mPageManager.fillStats(nvsStats);
  592. }
  593. esp_err_t Storage::calcEntriesInNamespace(uint8_t nsIndex, size_t& usedEntries)
  594. {
  595. usedEntries = 0;
  596. if (mState != StorageState::ACTIVE) {
  597. return ESP_ERR_NVS_NOT_INITIALIZED;
  598. }
  599. for (auto it = std::begin(mPageManager); it != std::end(mPageManager); ++it) {
  600. size_t itemIndex = 0;
  601. Item item;
  602. while (true) {
  603. auto err = it->findItem(nsIndex, ItemType::ANY, nullptr, itemIndex, item);
  604. if (err == ESP_ERR_NVS_NOT_FOUND) {
  605. break;
  606. }
  607. else if (err != ESP_OK) {
  608. return err;
  609. }
  610. usedEntries += item.span;
  611. itemIndex += item.span;
  612. if (itemIndex >= it->ENTRY_COUNT) break;
  613. }
  614. }
  615. return ESP_OK;
  616. }
  617. void Storage::fillEntryInfo(Item &item, nvs_entry_info_t &info)
  618. {
  619. info.type = static_cast<nvs_type_t>(item.datatype);
  620. strncpy(info.key, item.key, sizeof(info.key));
  621. for (auto &name : mNamespaces) {
  622. if(item.nsIndex == name.mIndex) {
  623. strncpy(info.namespace_name, name.mName, sizeof(info.namespace_name));
  624. break;
  625. }
  626. }
  627. }
  628. bool Storage::findEntry(nvs_opaque_iterator_t* it, const char* namespace_name)
  629. {
  630. it->entryIndex = 0;
  631. it->nsIndex = Page::NS_ANY;
  632. it->page = mPageManager.begin();
  633. if (namespace_name != nullptr) {
  634. if(createOrOpenNamespace(namespace_name, false, it->nsIndex) != ESP_OK) {
  635. return false;
  636. }
  637. }
  638. return nextEntry(it);
  639. }
  640. inline bool isIterableItem(Item& item)
  641. {
  642. return (item.nsIndex != 0 &&
  643. item.datatype != ItemType::BLOB &&
  644. item.datatype != ItemType::BLOB_IDX);
  645. }
  646. inline bool isMultipageBlob(Item& item)
  647. {
  648. return (item.datatype == ItemType::BLOB_DATA &&
  649. !(item.chunkIndex == static_cast<uint8_t>(VerOffset::VER_0_OFFSET)
  650. || item.chunkIndex == static_cast<uint8_t>(VerOffset::VER_1_OFFSET)));
  651. }
  652. bool Storage::nextEntry(nvs_opaque_iterator_t* it)
  653. {
  654. Item item;
  655. esp_err_t err;
  656. for (auto page = it->page; page != mPageManager.end(); ++page) {
  657. do {
  658. err = page->findItem(it->nsIndex, (ItemType)it->type, nullptr, it->entryIndex, item);
  659. it->entryIndex += item.span;
  660. if(err == ESP_OK && isIterableItem(item) && !isMultipageBlob(item)) {
  661. fillEntryInfo(item, it->entry_info);
  662. it->page = page;
  663. return true;
  664. }
  665. } while (err != ESP_ERR_NVS_NOT_FOUND);
  666. it->entryIndex = 0;
  667. }
  668. return false;
  669. }
  670. }