nvs_page.cpp 25 KB

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