nvs_page.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727
  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_page.hpp"
  14. #if defined(ESP_PLATFORM)
  15. #include <rom/crc.h>
  16. #else
  17. #include "crc.h"
  18. #endif
  19. #include <cstdio>
  20. namespace nvs
  21. {
  22. uint32_t Page::Header::calculateCrc32()
  23. {
  24. return crc32_le(0xffffffff,
  25. reinterpret_cast<uint8_t*>(this) + offsetof(Header, mSeqNumber),
  26. offsetof(Header, mCrc32) - offsetof(Header, mSeqNumber));
  27. }
  28. esp_err_t Page::load(uint32_t sectorNumber)
  29. {
  30. mBaseAddress = sectorNumber * SEC_SIZE;
  31. mUsedEntryCount = 0;
  32. mErasedEntryCount = 0;
  33. Header header;
  34. auto rc = spi_flash_read(mBaseAddress, reinterpret_cast<uint32_t*>(&header), sizeof(header));
  35. if (rc != ESP_OK) {
  36. mState = PageState::INVALID;
  37. return rc;
  38. }
  39. if (header.mState == PageState::UNINITIALIZED) {
  40. mState = header.mState;
  41. // check if the whole page is really empty
  42. // reading the whole page takes ~40 times less than erasing it
  43. uint32_t line[8];
  44. for (uint32_t i = 0; i < SPI_FLASH_SEC_SIZE; i += sizeof(line)) {
  45. rc = spi_flash_read(mBaseAddress + i, line, sizeof(line));
  46. if (rc != ESP_OK) {
  47. mState = PageState::INVALID;
  48. return rc;
  49. }
  50. if (std::any_of(line, line + 4, [](uint32_t val) -> bool { return val != 0xffffffff; })) {
  51. // page isn't as empty after all, mark it as corrupted
  52. mState = PageState::CORRUPT;
  53. break;
  54. }
  55. }
  56. }
  57. else if (header.mCrc32 != header.calculateCrc32()) {
  58. header.mState = PageState::CORRUPT;
  59. }
  60. else {
  61. mState = header.mState;
  62. mSeqNumber = header.mSeqNumber;
  63. }
  64. switch (mState) {
  65. case PageState::UNINITIALIZED:
  66. break;
  67. case PageState::FULL:
  68. case PageState::ACTIVE:
  69. case PageState::FREEING:
  70. mLoadEntryTable();
  71. break;
  72. default:
  73. mState = PageState::CORRUPT;
  74. break;
  75. }
  76. return ESP_OK;
  77. }
  78. esp_err_t Page::writeEntry(const Item& item)
  79. {
  80. auto rc = spi_flash_write(getEntryAddress(mNextFreeEntry), reinterpret_cast<const uint32_t*>(&item), sizeof(item));
  81. if (rc != ESP_OK) {
  82. mState = PageState::INVALID;
  83. return rc;
  84. }
  85. auto err = alterEntryState(mNextFreeEntry, EntryState::WRITTEN);
  86. if (err != ESP_OK) {
  87. return err;
  88. }
  89. if (mFirstUsedEntry == INVALID_ENTRY) {
  90. mFirstUsedEntry = mNextFreeEntry;
  91. }
  92. ++mUsedEntryCount;
  93. ++mNextFreeEntry;
  94. return ESP_OK;
  95. }
  96. esp_err_t Page::writeItem(uint8_t nsIndex, ItemType datatype, const char* key, const void* data, size_t dataSize)
  97. {
  98. Item item;
  99. esp_err_t err;
  100. if (mState == PageState::UNINITIALIZED) {
  101. err = initialize();
  102. if (err != ESP_OK) {
  103. return err;
  104. }
  105. }
  106. if (mState == PageState::FULL) {
  107. return ESP_ERR_NVS_PAGE_FULL;
  108. }
  109. const size_t keySize = strlen(key);
  110. if (keySize > Item::MAX_KEY_LENGTH) {
  111. return ESP_ERR_NVS_KEY_TOO_LONG;
  112. }
  113. size_t totalSize = ENTRY_SIZE;
  114. size_t entriesCount = 1;
  115. if (datatype == ItemType::SZ || datatype == ItemType::BLOB) {
  116. size_t roundedSize = (dataSize + ENTRY_SIZE - 1) & ~(ENTRY_SIZE - 1);
  117. totalSize += roundedSize;
  118. entriesCount += roundedSize / ENTRY_SIZE;
  119. }
  120. // primitive types should fit into one entry
  121. assert(totalSize == ENTRY_SIZE || datatype == ItemType::BLOB || datatype == ItemType::SZ);
  122. if (mNextFreeEntry == INVALID_ENTRY || mNextFreeEntry + entriesCount > ENTRY_COUNT) {
  123. // page will not fit this amount of data
  124. return ESP_ERR_NVS_PAGE_FULL;
  125. }
  126. // write first item
  127. item.nsIndex = nsIndex;
  128. item.datatype = datatype;
  129. item.span = (totalSize + ENTRY_SIZE - 1) / ENTRY_SIZE;
  130. item.reserved = 0xff;
  131. std::fill_n(reinterpret_cast<uint32_t*>(item.key), sizeof(item.key) / 4, 0xffffffff);
  132. std::fill_n(reinterpret_cast<uint32_t*>(item.data), sizeof(item.data) / 4, 0xffffffff);
  133. strlcpy(item.key, key, Item::MAX_KEY_LENGTH + 1);
  134. if (datatype != ItemType::SZ && datatype != ItemType::BLOB) {
  135. memcpy(item.data, data, dataSize);
  136. item.crc32 = item.calculateCrc32();
  137. err = writeEntry(item);
  138. if (err != ESP_OK) {
  139. return err;
  140. }
  141. } else {
  142. const uint8_t* src = reinterpret_cast<const uint8_t*>(data);
  143. item.varLength.dataCrc32 = Item::calculateCrc32(src, dataSize);
  144. item.varLength.dataSize = dataSize;
  145. item.varLength.reserved2 = 0xffff;
  146. item.crc32 = item.calculateCrc32();
  147. err = writeEntry(item);
  148. if (err != ESP_OK) {
  149. return err;
  150. }
  151. size_t left = dataSize;
  152. while (left != 0) {
  153. size_t willWrite = Page::ENTRY_SIZE;
  154. willWrite = (left < willWrite)?left:willWrite;
  155. memcpy(item.rawData, src, willWrite);
  156. src += willWrite;
  157. left -= willWrite;
  158. err = writeEntry(item);
  159. if (err != ESP_OK) {
  160. return err;
  161. }
  162. }
  163. }
  164. return ESP_OK;
  165. }
  166. esp_err_t Page::readItem(uint8_t nsIndex, ItemType datatype, const char* key, void* data, size_t dataSize)
  167. {
  168. size_t index = 0;
  169. Item item;
  170. esp_err_t rc = findItem(nsIndex, datatype, key, index, item);
  171. if (rc != ESP_OK) {
  172. return rc;
  173. }
  174. if (datatype != ItemType::SZ && datatype != ItemType::BLOB) {
  175. if (dataSize != getAlignmentForType(datatype)) {
  176. return ESP_ERR_NVS_TYPE_MISMATCH;
  177. }
  178. memcpy(data, item.data, dataSize);
  179. return ESP_OK;
  180. }
  181. if (dataSize < static_cast<size_t>(item.varLength.dataSize)) {
  182. return ESP_ERR_NVS_INVALID_LENGTH;
  183. }
  184. uint8_t* dst = reinterpret_cast<uint8_t*>(data);
  185. size_t left = item.varLength.dataSize;
  186. for (size_t i = index + 1; i < index + item.span; ++i) {
  187. Item ditem;
  188. rc = readEntry(i, ditem);
  189. if (rc != ESP_OK) {
  190. return rc;
  191. }
  192. size_t willCopy = ENTRY_SIZE;
  193. willCopy = (left < willCopy)?left:willCopy;
  194. memcpy(dst, ditem.rawData, willCopy);
  195. left -= willCopy;
  196. dst += willCopy;
  197. }
  198. if (Item::calculateCrc32(reinterpret_cast<uint8_t*>(data), item.varLength.dataSize) != item.varLength.dataCrc32) {
  199. rc = eraseEntryAndSpan(index);
  200. if (rc != ESP_OK) {
  201. return rc;
  202. }
  203. return ESP_ERR_NVS_NOT_FOUND;
  204. }
  205. return ESP_OK;
  206. }
  207. esp_err_t Page::eraseItem(uint8_t nsIndex, ItemType datatype, const char* key)
  208. {
  209. size_t index = 0;
  210. Item item;
  211. esp_err_t rc = findItem(nsIndex, datatype, key, index, item);
  212. if (rc != ESP_OK) {
  213. return rc;
  214. }
  215. if (CachedFindInfo(nsIndex, datatype, key) == mFindInfo) {
  216. invalidateCache();
  217. }
  218. return eraseEntryAndSpan(index);
  219. }
  220. esp_err_t Page::findItem(uint8_t nsIndex, ItemType datatype, const char* key)
  221. {
  222. size_t index = 0;
  223. Item item;
  224. return findItem(nsIndex, datatype, key, index, item);
  225. }
  226. esp_err_t Page::eraseEntry(size_t index)
  227. {
  228. auto state = mEntryTable.get(index);
  229. assert(state == EntryState::WRITTEN || state == EntryState::EMPTY);
  230. auto rc = alterEntryState(index, EntryState::ERASED);
  231. if (rc != ESP_OK) {
  232. return rc;
  233. }
  234. return ESP_OK;
  235. }
  236. esp_err_t Page::eraseEntryAndSpan(size_t index)
  237. {
  238. auto state = mEntryTable.get(index);
  239. assert(state == EntryState::WRITTEN || state == EntryState::EMPTY);
  240. size_t span = 1;
  241. if (state == EntryState::WRITTEN) {
  242. Item item;
  243. auto rc = readEntry(index, item);
  244. if (rc != ESP_OK) {
  245. return rc;
  246. }
  247. if (item.calculateCrc32() != item.crc32) {
  248. rc = alterEntryState(index, EntryState::ERASED);
  249. if (rc != ESP_OK) {
  250. return rc;
  251. }
  252. } else {
  253. span = item.span;
  254. for (ptrdiff_t i = index + span - 1; i >= static_cast<ptrdiff_t>(index); --i) {
  255. if (mEntryTable.get(i) == EntryState::WRITTEN) {
  256. --mUsedEntryCount;
  257. }
  258. rc = alterEntryState(i, EntryState::ERASED);
  259. if (rc != ESP_OK) {
  260. return rc;
  261. }
  262. ++mErasedEntryCount;
  263. }
  264. }
  265. }
  266. else {
  267. auto rc = alterEntryState(index, EntryState::ERASED);
  268. if (rc != ESP_OK) {
  269. return rc;
  270. }
  271. }
  272. if (index == mFirstUsedEntry) {
  273. updateFirstUsedEntry(index, span);
  274. }
  275. if (index + span > mNextFreeEntry) {
  276. mNextFreeEntry = index + span;
  277. }
  278. return ESP_OK;
  279. }
  280. void Page::updateFirstUsedEntry(size_t index, size_t span)
  281. {
  282. assert(index == mFirstUsedEntry);
  283. mFirstUsedEntry = INVALID_ENTRY;
  284. size_t end = mNextFreeEntry;
  285. if (end > ENTRY_COUNT) {
  286. end = ENTRY_COUNT;
  287. }
  288. for (size_t i = index + span; i < end; ++i) {
  289. if (mEntryTable.get(i) == EntryState::WRITTEN) {
  290. mFirstUsedEntry = i;
  291. break;
  292. }
  293. }
  294. }
  295. esp_err_t Page::moveItem(Page& other)
  296. {
  297. if (mFirstUsedEntry == INVALID_ENTRY) {
  298. return ESP_ERR_NVS_NOT_FOUND;
  299. }
  300. if (mFindInfo.itemIndex() == mFirstUsedEntry) {
  301. invalidateCache();
  302. }
  303. if (other.mState == PageState::UNINITIALIZED) {
  304. auto err = other.initialize();
  305. if (err != ESP_OK) {
  306. return err;
  307. }
  308. }
  309. Item entry;
  310. auto err = readEntry(mFirstUsedEntry, entry);
  311. if (err != ESP_OK) {
  312. return err;
  313. }
  314. err = other.writeEntry(entry);
  315. if (err != ESP_OK) {
  316. return err;
  317. }
  318. size_t span = entry.span;
  319. size_t end = mFirstUsedEntry + span;
  320. assert(mFirstUsedEntry != INVALID_ENTRY || span == 1);
  321. for (size_t i = mFirstUsedEntry + 1; i < end; ++i) {
  322. readEntry(i, entry);
  323. err = other.writeEntry(entry);
  324. if (err != ESP_OK) {
  325. return err;
  326. }
  327. }
  328. for (size_t i = mFirstUsedEntry; i < end; ++i) {
  329. err = eraseEntry(i);
  330. if (err != ESP_OK) {
  331. return err;
  332. }
  333. }
  334. updateFirstUsedEntry(mFirstUsedEntry, span);
  335. mErasedEntryCount += span;
  336. mUsedEntryCount -= span;
  337. return ESP_OK;
  338. }
  339. esp_err_t Page::mLoadEntryTable()
  340. {
  341. // for states where we actually care about data in the page, read entry state table
  342. if (mState == PageState::ACTIVE ||
  343. mState == PageState::FULL ||
  344. mState == PageState::FREEING) {
  345. auto rc = spi_flash_read(mBaseAddress + ENTRY_TABLE_OFFSET, mEntryTable.data(),
  346. static_cast<uint32_t>(mEntryTable.byteSize()));
  347. if (rc != ESP_OK) {
  348. mState = PageState::INVALID;
  349. return rc;
  350. }
  351. }
  352. mErasedEntryCount = 0;
  353. mUsedEntryCount = 0;
  354. for (size_t i = 0; i < ENTRY_COUNT; ++i) {
  355. auto s = mEntryTable.get(i);
  356. if (s == EntryState::WRITTEN) {
  357. if (mFirstUsedEntry == INVALID_ENTRY) {
  358. mFirstUsedEntry = i;
  359. }
  360. ++mUsedEntryCount;
  361. } else if (s == EntryState::ERASED) {
  362. ++mErasedEntryCount;
  363. }
  364. }
  365. // for PageState::ACTIVE, we may have more data written to this page
  366. // as such, we need to figure out where the first unused entry is
  367. if (mState == PageState::ACTIVE) {
  368. for (size_t i = 0; i < ENTRY_COUNT; ++i) {
  369. if (mEntryTable.get(i) == EntryState::EMPTY) {
  370. mNextFreeEntry = i;
  371. break;
  372. }
  373. }
  374. // however, if power failed after some data was written into the entry.
  375. // but before the entry state table was altered, the entry locacted via
  376. // entry state table may actually be half-written.
  377. // this is easy to check by reading EntryHeader (i.e. first word)
  378. if (mNextFreeEntry != INVALID_ENTRY) {
  379. uint32_t entryAddress = getEntryAddress(mNextFreeEntry);
  380. uint32_t header;
  381. auto rc = spi_flash_read(entryAddress, &header, sizeof(header));
  382. if (rc != ESP_OK) {
  383. mState = PageState::INVALID;
  384. return rc;
  385. }
  386. if (header != 0xffffffff) {
  387. auto err = alterEntryState(mNextFreeEntry, EntryState::ERASED);
  388. if (err != ESP_OK) {
  389. mState = PageState::INVALID;
  390. return err;
  391. }
  392. ++mNextFreeEntry;
  393. }
  394. }
  395. // check that all variable-length items are written or erased fully
  396. Item item;
  397. size_t lastItemIndex = INVALID_ENTRY;
  398. size_t end = mNextFreeEntry;
  399. if (end > ENTRY_COUNT) {
  400. end = ENTRY_COUNT;
  401. }
  402. for (size_t i = 0; i < end; ++i) {
  403. if (mEntryTable.get(i) == EntryState::ERASED) {
  404. lastItemIndex = INVALID_ENTRY;
  405. continue;
  406. }
  407. lastItemIndex = i;
  408. auto err = readEntry(i, item);
  409. if (err != ESP_OK) {
  410. mState = PageState::INVALID;
  411. return err;
  412. }
  413. if (item.crc32 != item.calculateCrc32()) {
  414. err = eraseEntryAndSpan(i);
  415. if (err != ESP_OK) {
  416. mState = PageState::INVALID;
  417. return err;
  418. }
  419. continue;
  420. }
  421. if (item.datatype != ItemType::BLOB && item.datatype != ItemType::SZ) {
  422. continue;
  423. }
  424. size_t span = item.span;
  425. bool needErase = false;
  426. for (size_t j = i; j < i + span; ++j) {
  427. if (mEntryTable.get(j) != EntryState::WRITTEN) {
  428. needErase = true;
  429. lastItemIndex = INVALID_ENTRY;
  430. break;
  431. }
  432. }
  433. if (needErase) {
  434. eraseEntryAndSpan(i);
  435. }
  436. i += span - 1;
  437. }
  438. // check that last item is not duplicate
  439. if (lastItemIndex != INVALID_ENTRY) {
  440. size_t findItemIndex = 0;
  441. Item dupItem;
  442. if (findItem(item.nsIndex, item.datatype, item.key, findItemIndex, dupItem) == ESP_OK) {
  443. if (findItemIndex < lastItemIndex) {
  444. auto err = eraseEntryAndSpan(findItemIndex);
  445. if (err != ESP_OK) {
  446. mState = PageState::INVALID;
  447. return err;
  448. }
  449. }
  450. }
  451. }
  452. }
  453. return ESP_OK;
  454. }
  455. esp_err_t Page::initialize()
  456. {
  457. assert(mState == PageState::UNINITIALIZED);
  458. mState = PageState::ACTIVE;
  459. Header header;
  460. header.mState = mState;
  461. header.mSeqNumber = mSeqNumber;
  462. header.mCrc32 = header.calculateCrc32();
  463. auto rc = spi_flash_write(mBaseAddress, reinterpret_cast<uint32_t*>(&header), sizeof(header));
  464. if (rc != ESP_OK) {
  465. mState = PageState::INVALID;
  466. return rc;
  467. }
  468. mNextFreeEntry = 0;
  469. std::fill_n(mEntryTable.data(), mEntryTable.byteSize() / sizeof(uint32_t), 0xffffffff);
  470. invalidateCache();
  471. return ESP_OK;
  472. }
  473. esp_err_t Page::alterEntryState(size_t index, EntryState state)
  474. {
  475. assert(index < ENTRY_COUNT);
  476. mEntryTable.set(index, state);
  477. size_t wordToWrite = mEntryTable.getWordIndex(index);
  478. uint32_t word = mEntryTable.data()[wordToWrite];
  479. auto rc = spi_flash_write(mBaseAddress + ENTRY_TABLE_OFFSET + static_cast<uint32_t>(wordToWrite) * 4, &word, 4);
  480. if (rc != ESP_OK) {
  481. mState = PageState::INVALID;
  482. return rc;
  483. }
  484. return ESP_OK;
  485. }
  486. esp_err_t Page::alterPageState(PageState state)
  487. {
  488. auto rc = spi_flash_write(mBaseAddress, reinterpret_cast<uint32_t*>(&state), sizeof(state));
  489. if (rc != ESP_OK) {
  490. mState = PageState::INVALID;
  491. return rc;
  492. }
  493. mState = (PageState) state;
  494. return ESP_OK;
  495. }
  496. esp_err_t Page::readEntry(size_t index, Item& dst) const
  497. {
  498. auto rc = spi_flash_read(getEntryAddress(index), reinterpret_cast<uint32_t*>(&dst), sizeof(dst));
  499. if (rc != ESP_OK) {
  500. return rc;
  501. }
  502. return ESP_OK;
  503. }
  504. esp_err_t Page::findItem(uint8_t nsIndex, ItemType datatype, const char* key, size_t &itemIndex, Item& item)
  505. {
  506. if (mState == PageState::CORRUPT || mState == PageState::INVALID || mState == PageState::UNINITIALIZED) {
  507. return ESP_ERR_NVS_NOT_FOUND;
  508. }
  509. if (itemIndex >= ENTRY_COUNT) {
  510. return ESP_ERR_NVS_NOT_FOUND;
  511. }
  512. CachedFindInfo findInfo(nsIndex, datatype, key);
  513. if (mFindInfo == findInfo) {
  514. itemIndex = mFindInfo.itemIndex();
  515. }
  516. size_t start = mFirstUsedEntry;
  517. if (itemIndex > mFirstUsedEntry && itemIndex < ENTRY_COUNT) {
  518. start = itemIndex;
  519. }
  520. size_t end = mNextFreeEntry;
  521. if (end > ENTRY_COUNT) {
  522. end = ENTRY_COUNT;
  523. }
  524. size_t next;
  525. for (size_t i = start; i < end; i = next) {
  526. next = i + 1;
  527. if (mEntryTable.get(i) != EntryState::WRITTEN) {
  528. continue;
  529. }
  530. auto rc = readEntry(i, item);
  531. if (rc != ESP_OK) {
  532. mState = PageState::INVALID;
  533. return rc;
  534. }
  535. auto crc32 = item.calculateCrc32();
  536. if (item.crc32 != crc32) {
  537. eraseEntryAndSpan(i);
  538. continue;
  539. }
  540. if (item.datatype == ItemType::BLOB || item.datatype == ItemType::SZ) {
  541. next = i + item.span;
  542. }
  543. if (nsIndex != NS_ANY && item.nsIndex != nsIndex) {
  544. continue;
  545. }
  546. if (key != nullptr && strncmp(key, item.key, Item::MAX_KEY_LENGTH) != 0) {
  547. continue;
  548. }
  549. if (datatype != ItemType::ANY && item.datatype != datatype) {
  550. return ESP_ERR_NVS_TYPE_MISMATCH;
  551. }
  552. itemIndex = i;
  553. findInfo.setItemIndex(static_cast<uint32_t>(itemIndex));
  554. mFindInfo = findInfo;
  555. return ESP_OK;
  556. }
  557. return ESP_ERR_NVS_NOT_FOUND;
  558. }
  559. esp_err_t Page::getSeqNumber(uint32_t& seqNumber) const
  560. {
  561. if (mState != PageState::UNINITIALIZED && mState != PageState::INVALID && mState != PageState::CORRUPT) {
  562. seqNumber = mSeqNumber;
  563. return ESP_OK;
  564. }
  565. return ESP_ERR_NVS_NOT_INITIALIZED;
  566. }
  567. esp_err_t Page::setSeqNumber(uint32_t seqNumber)
  568. {
  569. if (mState != PageState::UNINITIALIZED) {
  570. return ESP_ERR_NVS_INVALID_STATE;
  571. }
  572. mSeqNumber = seqNumber;
  573. return ESP_OK;
  574. }
  575. esp_err_t Page::erase()
  576. {
  577. auto sector = mBaseAddress / SPI_FLASH_SEC_SIZE;
  578. auto rc = spi_flash_erase_sector(sector);
  579. if (rc != ESP_OK) {
  580. mState = PageState::INVALID;
  581. return rc;
  582. }
  583. return load(sector);
  584. }
  585. esp_err_t Page::markFreeing()
  586. {
  587. if (mState != PageState::FULL && mState != PageState::ACTIVE) {
  588. return ESP_ERR_NVS_INVALID_STATE;
  589. }
  590. return alterPageState(PageState::FREEING);
  591. }
  592. esp_err_t Page::markFull()
  593. {
  594. if (mState != PageState::ACTIVE) {
  595. return ESP_ERR_NVS_INVALID_STATE;
  596. }
  597. return alterPageState(PageState::FULL);
  598. }
  599. void Page::invalidateCache()
  600. {
  601. mFindInfo = CachedFindInfo();
  602. }
  603. void Page::debugDump() const
  604. {
  605. printf("state=%x addr=%x seq=%d\nfirstUsed=%d nextFree=%d used=%d erased=%d\n", mState, mBaseAddress, mSeqNumber, static_cast<int>(mFirstUsedEntry), static_cast<int>(mNextFreeEntry), mUsedEntryCount, mErasedEntryCount);
  606. size_t skip = 0;
  607. for (size_t i = 0; i < ENTRY_COUNT; ++i) {
  608. printf("%3d: ", static_cast<int>(i));
  609. EntryState state = mEntryTable.get(i);
  610. if (state == EntryState::EMPTY) {
  611. printf("E\n");
  612. }
  613. else if (state == EntryState::ERASED) {
  614. printf("X\n");
  615. }
  616. else if (state == EntryState::WRITTEN) {
  617. Item item;
  618. readEntry(i, item);
  619. if (skip == 0) {
  620. printf("W ns=%2u type=%2u span=%3u key=\"%s\"\n", item.nsIndex, static_cast<unsigned>(item.datatype), item.span, item.key);
  621. skip = item.span - 1;
  622. }
  623. else {
  624. printf("D\n");
  625. skip--;
  626. }
  627. }
  628. }
  629. }
  630. } // namespace nvs