nvs_storage.cpp 25 KB

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