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