nvs_storage.cpp 25 KB

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