nvs_page.cpp 23 KB

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