Fork of the espurna firmware for `mhsw` switches
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

651 lines
19 KiB

Rework settings (#2282) * wip based on early draft. todo benchmarking * fixup eraser, assume keys are unique * fix cursor copy, test removal at random * small benchmark via permutations. todo lambdas and novirtual * fix empty condition / reset * overwrite optimizations, fix move offsets overflows * ...erase using 0xff instead of 0 * test the theory with code, different length kv were bugged * try to check for out-of-bounds writes / reads * style * trying to fix mover again * clarify length, defend against reading len on edge * fix uncommited rewind change * prove space assumptions * more concise traces, fix move condition (agrh!!!) * slightly more internal knowledge (estimates API?) * make sure cursor is only valid within the range * ensure 0 does not blow things * go back up * cursor comments * comments * rewrite writes through cursor * in del too * estimate kv storage requirements, return available size * move raw erase / move into a method, allow ::set to avoid scanning storage twice * refactor naming, use in code * amend storage slicing test * fix crash handler offsets, cleanup configuration * start -> begin * eeprom readiness * dependencies * unused * SPI_FLASH constants for older Core * vtables -> templates * less include dependencies * gcov help, move estimate outside of the class * writer position can never match, use begin + offset * tweak save_crash to trigger only once in a serious crash * doh, header function should be inline * foreach api, tweak structs for public api * use test helper class * when not using foreach, move cursor reset closer to the loop using read_kv * coverage comments, fix typo in tests decltype * ensure set() does not break with offset * make codacy happy again
4 years ago
Rework settings (#2282) * wip based on early draft. todo benchmarking * fixup eraser, assume keys are unique * fix cursor copy, test removal at random * small benchmark via permutations. todo lambdas and novirtual * fix empty condition / reset * overwrite optimizations, fix move offsets overflows * ...erase using 0xff instead of 0 * test the theory with code, different length kv were bugged * try to check for out-of-bounds writes / reads * style * trying to fix mover again * clarify length, defend against reading len on edge * fix uncommited rewind change * prove space assumptions * more concise traces, fix move condition (agrh!!!) * slightly more internal knowledge (estimates API?) * make sure cursor is only valid within the range * ensure 0 does not blow things * go back up * cursor comments * comments * rewrite writes through cursor * in del too * estimate kv storage requirements, return available size * move raw erase / move into a method, allow ::set to avoid scanning storage twice * refactor naming, use in code * amend storage slicing test * fix crash handler offsets, cleanup configuration * start -> begin * eeprom readiness * dependencies * unused * SPI_FLASH constants for older Core * vtables -> templates * less include dependencies * gcov help, move estimate outside of the class * writer position can never match, use begin + offset * tweak save_crash to trigger only once in a serious crash * doh, header function should be inline * foreach api, tweak structs for public api * use test helper class * when not using foreach, move cursor reset closer to the loop using read_kv * coverage comments, fix typo in tests decltype * ensure set() does not break with offset * make codacy happy again
4 years ago
Rework settings (#2282) * wip based on early draft. todo benchmarking * fixup eraser, assume keys are unique * fix cursor copy, test removal at random * small benchmark via permutations. todo lambdas and novirtual * fix empty condition / reset * overwrite optimizations, fix move offsets overflows * ...erase using 0xff instead of 0 * test the theory with code, different length kv were bugged * try to check for out-of-bounds writes / reads * style * trying to fix mover again * clarify length, defend against reading len on edge * fix uncommited rewind change * prove space assumptions * more concise traces, fix move condition (agrh!!!) * slightly more internal knowledge (estimates API?) * make sure cursor is only valid within the range * ensure 0 does not blow things * go back up * cursor comments * comments * rewrite writes through cursor * in del too * estimate kv storage requirements, return available size * move raw erase / move into a method, allow ::set to avoid scanning storage twice * refactor naming, use in code * amend storage slicing test * fix crash handler offsets, cleanup configuration * start -> begin * eeprom readiness * dependencies * unused * SPI_FLASH constants for older Core * vtables -> templates * less include dependencies * gcov help, move estimate outside of the class * writer position can never match, use begin + offset * tweak save_crash to trigger only once in a serious crash * doh, header function should be inline * foreach api, tweak structs for public api * use test helper class * when not using foreach, move cursor reset closer to the loop using read_kv * coverage comments, fix typo in tests decltype * ensure set() does not break with offset * make codacy happy again
4 years ago
Rework settings (#2282) * wip based on early draft. todo benchmarking * fixup eraser, assume keys are unique * fix cursor copy, test removal at random * small benchmark via permutations. todo lambdas and novirtual * fix empty condition / reset * overwrite optimizations, fix move offsets overflows * ...erase using 0xff instead of 0 * test the theory with code, different length kv were bugged * try to check for out-of-bounds writes / reads * style * trying to fix mover again * clarify length, defend against reading len on edge * fix uncommited rewind change * prove space assumptions * more concise traces, fix move condition (agrh!!!) * slightly more internal knowledge (estimates API?) * make sure cursor is only valid within the range * ensure 0 does not blow things * go back up * cursor comments * comments * rewrite writes through cursor * in del too * estimate kv storage requirements, return available size * move raw erase / move into a method, allow ::set to avoid scanning storage twice * refactor naming, use in code * amend storage slicing test * fix crash handler offsets, cleanup configuration * start -> begin * eeprom readiness * dependencies * unused * SPI_FLASH constants for older Core * vtables -> templates * less include dependencies * gcov help, move estimate outside of the class * writer position can never match, use begin + offset * tweak save_crash to trigger only once in a serious crash * doh, header function should be inline * foreach api, tweak structs for public api * use test helper class * when not using foreach, move cursor reset closer to the loop using read_kv * coverage comments, fix typo in tests decltype * ensure set() does not break with offset * make codacy happy again
4 years ago
Rework settings (#2282) * wip based on early draft. todo benchmarking * fixup eraser, assume keys are unique * fix cursor copy, test removal at random * small benchmark via permutations. todo lambdas and novirtual * fix empty condition / reset * overwrite optimizations, fix move offsets overflows * ...erase using 0xff instead of 0 * test the theory with code, different length kv were bugged * try to check for out-of-bounds writes / reads * style * trying to fix mover again * clarify length, defend against reading len on edge * fix uncommited rewind change * prove space assumptions * more concise traces, fix move condition (agrh!!!) * slightly more internal knowledge (estimates API?) * make sure cursor is only valid within the range * ensure 0 does not blow things * go back up * cursor comments * comments * rewrite writes through cursor * in del too * estimate kv storage requirements, return available size * move raw erase / move into a method, allow ::set to avoid scanning storage twice * refactor naming, use in code * amend storage slicing test * fix crash handler offsets, cleanup configuration * start -> begin * eeprom readiness * dependencies * unused * SPI_FLASH constants for older Core * vtables -> templates * less include dependencies * gcov help, move estimate outside of the class * writer position can never match, use begin + offset * tweak save_crash to trigger only once in a serious crash * doh, header function should be inline * foreach api, tweak structs for public api * use test helper class * when not using foreach, move cursor reset closer to the loop using read_kv * coverage comments, fix typo in tests decltype * ensure set() does not break with offset * make codacy happy again
4 years ago
Rework settings (#2282) * wip based on early draft. todo benchmarking * fixup eraser, assume keys are unique * fix cursor copy, test removal at random * small benchmark via permutations. todo lambdas and novirtual * fix empty condition / reset * overwrite optimizations, fix move offsets overflows * ...erase using 0xff instead of 0 * test the theory with code, different length kv were bugged * try to check for out-of-bounds writes / reads * style * trying to fix mover again * clarify length, defend against reading len on edge * fix uncommited rewind change * prove space assumptions * more concise traces, fix move condition (agrh!!!) * slightly more internal knowledge (estimates API?) * make sure cursor is only valid within the range * ensure 0 does not blow things * go back up * cursor comments * comments * rewrite writes through cursor * in del too * estimate kv storage requirements, return available size * move raw erase / move into a method, allow ::set to avoid scanning storage twice * refactor naming, use in code * amend storage slicing test * fix crash handler offsets, cleanup configuration * start -> begin * eeprom readiness * dependencies * unused * SPI_FLASH constants for older Core * vtables -> templates * less include dependencies * gcov help, move estimate outside of the class * writer position can never match, use begin + offset * tweak save_crash to trigger only once in a serious crash * doh, header function should be inline * foreach api, tweak structs for public api * use test helper class * when not using foreach, move cursor reset closer to the loop using read_kv * coverage comments, fix typo in tests decltype * ensure set() does not break with offset * make codacy happy again
4 years ago
Rework settings (#2282) * wip based on early draft. todo benchmarking * fixup eraser, assume keys are unique * fix cursor copy, test removal at random * small benchmark via permutations. todo lambdas and novirtual * fix empty condition / reset * overwrite optimizations, fix move offsets overflows * ...erase using 0xff instead of 0 * test the theory with code, different length kv were bugged * try to check for out-of-bounds writes / reads * style * trying to fix mover again * clarify length, defend against reading len on edge * fix uncommited rewind change * prove space assumptions * more concise traces, fix move condition (agrh!!!) * slightly more internal knowledge (estimates API?) * make sure cursor is only valid within the range * ensure 0 does not blow things * go back up * cursor comments * comments * rewrite writes through cursor * in del too * estimate kv storage requirements, return available size * move raw erase / move into a method, allow ::set to avoid scanning storage twice * refactor naming, use in code * amend storage slicing test * fix crash handler offsets, cleanup configuration * start -> begin * eeprom readiness * dependencies * unused * SPI_FLASH constants for older Core * vtables -> templates * less include dependencies * gcov help, move estimate outside of the class * writer position can never match, use begin + offset * tweak save_crash to trigger only once in a serious crash * doh, header function should be inline * foreach api, tweak structs for public api * use test helper class * when not using foreach, move cursor reset closer to the loop using read_kv * coverage comments, fix typo in tests decltype * ensure set() does not break with offset * make codacy happy again
4 years ago
Rework settings (#2282) * wip based on early draft. todo benchmarking * fixup eraser, assume keys are unique * fix cursor copy, test removal at random * small benchmark via permutations. todo lambdas and novirtual * fix empty condition / reset * overwrite optimizations, fix move offsets overflows * ...erase using 0xff instead of 0 * test the theory with code, different length kv were bugged * try to check for out-of-bounds writes / reads * style * trying to fix mover again * clarify length, defend against reading len on edge * fix uncommited rewind change * prove space assumptions * more concise traces, fix move condition (agrh!!!) * slightly more internal knowledge (estimates API?) * make sure cursor is only valid within the range * ensure 0 does not blow things * go back up * cursor comments * comments * rewrite writes through cursor * in del too * estimate kv storage requirements, return available size * move raw erase / move into a method, allow ::set to avoid scanning storage twice * refactor naming, use in code * amend storage slicing test * fix crash handler offsets, cleanup configuration * start -> begin * eeprom readiness * dependencies * unused * SPI_FLASH constants for older Core * vtables -> templates * less include dependencies * gcov help, move estimate outside of the class * writer position can never match, use begin + offset * tweak save_crash to trigger only once in a serious crash * doh, header function should be inline * foreach api, tweak structs for public api * use test helper class * when not using foreach, move cursor reset closer to the loop using read_kv * coverage comments, fix typo in tests decltype * ensure set() does not break with offset * make codacy happy again
4 years ago
Rework settings (#2282) * wip based on early draft. todo benchmarking * fixup eraser, assume keys are unique * fix cursor copy, test removal at random * small benchmark via permutations. todo lambdas and novirtual * fix empty condition / reset * overwrite optimizations, fix move offsets overflows * ...erase using 0xff instead of 0 * test the theory with code, different length kv were bugged * try to check for out-of-bounds writes / reads * style * trying to fix mover again * clarify length, defend against reading len on edge * fix uncommited rewind change * prove space assumptions * more concise traces, fix move condition (agrh!!!) * slightly more internal knowledge (estimates API?) * make sure cursor is only valid within the range * ensure 0 does not blow things * go back up * cursor comments * comments * rewrite writes through cursor * in del too * estimate kv storage requirements, return available size * move raw erase / move into a method, allow ::set to avoid scanning storage twice * refactor naming, use in code * amend storage slicing test * fix crash handler offsets, cleanup configuration * start -> begin * eeprom readiness * dependencies * unused * SPI_FLASH constants for older Core * vtables -> templates * less include dependencies * gcov help, move estimate outside of the class * writer position can never match, use begin + offset * tweak save_crash to trigger only once in a serious crash * doh, header function should be inline * foreach api, tweak structs for public api * use test helper class * when not using foreach, move cursor reset closer to the loop using read_kv * coverage comments, fix typo in tests decltype * ensure set() does not break with offset * make codacy happy again
4 years ago
Rework settings (#2282) * wip based on early draft. todo benchmarking * fixup eraser, assume keys are unique * fix cursor copy, test removal at random * small benchmark via permutations. todo lambdas and novirtual * fix empty condition / reset * overwrite optimizations, fix move offsets overflows * ...erase using 0xff instead of 0 * test the theory with code, different length kv were bugged * try to check for out-of-bounds writes / reads * style * trying to fix mover again * clarify length, defend against reading len on edge * fix uncommited rewind change * prove space assumptions * more concise traces, fix move condition (agrh!!!) * slightly more internal knowledge (estimates API?) * make sure cursor is only valid within the range * ensure 0 does not blow things * go back up * cursor comments * comments * rewrite writes through cursor * in del too * estimate kv storage requirements, return available size * move raw erase / move into a method, allow ::set to avoid scanning storage twice * refactor naming, use in code * amend storage slicing test * fix crash handler offsets, cleanup configuration * start -> begin * eeprom readiness * dependencies * unused * SPI_FLASH constants for older Core * vtables -> templates * less include dependencies * gcov help, move estimate outside of the class * writer position can never match, use begin + offset * tweak save_crash to trigger only once in a serious crash * doh, header function should be inline * foreach api, tweak structs for public api * use test helper class * when not using foreach, move cursor reset closer to the loop using read_kv * coverage comments, fix typo in tests decltype * ensure set() does not break with offset * make codacy happy again
4 years ago
Rework settings (#2282) * wip based on early draft. todo benchmarking * fixup eraser, assume keys are unique * fix cursor copy, test removal at random * small benchmark via permutations. todo lambdas and novirtual * fix empty condition / reset * overwrite optimizations, fix move offsets overflows * ...erase using 0xff instead of 0 * test the theory with code, different length kv were bugged * try to check for out-of-bounds writes / reads * style * trying to fix mover again * clarify length, defend against reading len on edge * fix uncommited rewind change * prove space assumptions * more concise traces, fix move condition (agrh!!!) * slightly more internal knowledge (estimates API?) * make sure cursor is only valid within the range * ensure 0 does not blow things * go back up * cursor comments * comments * rewrite writes through cursor * in del too * estimate kv storage requirements, return available size * move raw erase / move into a method, allow ::set to avoid scanning storage twice * refactor naming, use in code * amend storage slicing test * fix crash handler offsets, cleanup configuration * start -> begin * eeprom readiness * dependencies * unused * SPI_FLASH constants for older Core * vtables -> templates * less include dependencies * gcov help, move estimate outside of the class * writer position can never match, use begin + offset * tweak save_crash to trigger only once in a serious crash * doh, header function should be inline * foreach api, tweak structs for public api * use test helper class * when not using foreach, move cursor reset closer to the loop using read_kv * coverage comments, fix typo in tests decltype * ensure set() does not break with offset * make codacy happy again
4 years ago
Rework settings (#2282) * wip based on early draft. todo benchmarking * fixup eraser, assume keys are unique * fix cursor copy, test removal at random * small benchmark via permutations. todo lambdas and novirtual * fix empty condition / reset * overwrite optimizations, fix move offsets overflows * ...erase using 0xff instead of 0 * test the theory with code, different length kv were bugged * try to check for out-of-bounds writes / reads * style * trying to fix mover again * clarify length, defend against reading len on edge * fix uncommited rewind change * prove space assumptions * more concise traces, fix move condition (agrh!!!) * slightly more internal knowledge (estimates API?) * make sure cursor is only valid within the range * ensure 0 does not blow things * go back up * cursor comments * comments * rewrite writes through cursor * in del too * estimate kv storage requirements, return available size * move raw erase / move into a method, allow ::set to avoid scanning storage twice * refactor naming, use in code * amend storage slicing test * fix crash handler offsets, cleanup configuration * start -> begin * eeprom readiness * dependencies * unused * SPI_FLASH constants for older Core * vtables -> templates * less include dependencies * gcov help, move estimate outside of the class * writer position can never match, use begin + offset * tweak save_crash to trigger only once in a serious crash * doh, header function should be inline * foreach api, tweak structs for public api * use test helper class * when not using foreach, move cursor reset closer to the loop using read_kv * coverage comments, fix typo in tests decltype * ensure set() does not break with offset * make codacy happy again
4 years ago
Rework settings (#2282) * wip based on early draft. todo benchmarking * fixup eraser, assume keys are unique * fix cursor copy, test removal at random * small benchmark via permutations. todo lambdas and novirtual * fix empty condition / reset * overwrite optimizations, fix move offsets overflows * ...erase using 0xff instead of 0 * test the theory with code, different length kv were bugged * try to check for out-of-bounds writes / reads * style * trying to fix mover again * clarify length, defend against reading len on edge * fix uncommited rewind change * prove space assumptions * more concise traces, fix move condition (agrh!!!) * slightly more internal knowledge (estimates API?) * make sure cursor is only valid within the range * ensure 0 does not blow things * go back up * cursor comments * comments * rewrite writes through cursor * in del too * estimate kv storage requirements, return available size * move raw erase / move into a method, allow ::set to avoid scanning storage twice * refactor naming, use in code * amend storage slicing test * fix crash handler offsets, cleanup configuration * start -> begin * eeprom readiness * dependencies * unused * SPI_FLASH constants for older Core * vtables -> templates * less include dependencies * gcov help, move estimate outside of the class * writer position can never match, use begin + offset * tweak save_crash to trigger only once in a serious crash * doh, header function should be inline * foreach api, tweak structs for public api * use test helper class * when not using foreach, move cursor reset closer to the loop using read_kv * coverage comments, fix typo in tests decltype * ensure set() does not break with offset * make codacy happy again
4 years ago
Rework settings (#2282) * wip based on early draft. todo benchmarking * fixup eraser, assume keys are unique * fix cursor copy, test removal at random * small benchmark via permutations. todo lambdas and novirtual * fix empty condition / reset * overwrite optimizations, fix move offsets overflows * ...erase using 0xff instead of 0 * test the theory with code, different length kv were bugged * try to check for out-of-bounds writes / reads * style * trying to fix mover again * clarify length, defend against reading len on edge * fix uncommited rewind change * prove space assumptions * more concise traces, fix move condition (agrh!!!) * slightly more internal knowledge (estimates API?) * make sure cursor is only valid within the range * ensure 0 does not blow things * go back up * cursor comments * comments * rewrite writes through cursor * in del too * estimate kv storage requirements, return available size * move raw erase / move into a method, allow ::set to avoid scanning storage twice * refactor naming, use in code * amend storage slicing test * fix crash handler offsets, cleanup configuration * start -> begin * eeprom readiness * dependencies * unused * SPI_FLASH constants for older Core * vtables -> templates * less include dependencies * gcov help, move estimate outside of the class * writer position can never match, use begin + offset * tweak save_crash to trigger only once in a serious crash * doh, header function should be inline * foreach api, tweak structs for public api * use test helper class * when not using foreach, move cursor reset closer to the loop using read_kv * coverage comments, fix typo in tests decltype * ensure set() does not break with offset * make codacy happy again
4 years ago
Rework settings (#2282) * wip based on early draft. todo benchmarking * fixup eraser, assume keys are unique * fix cursor copy, test removal at random * small benchmark via permutations. todo lambdas and novirtual * fix empty condition / reset * overwrite optimizations, fix move offsets overflows * ...erase using 0xff instead of 0 * test the theory with code, different length kv were bugged * try to check for out-of-bounds writes / reads * style * trying to fix mover again * clarify length, defend against reading len on edge * fix uncommited rewind change * prove space assumptions * more concise traces, fix move condition (agrh!!!) * slightly more internal knowledge (estimates API?) * make sure cursor is only valid within the range * ensure 0 does not blow things * go back up * cursor comments * comments * rewrite writes through cursor * in del too * estimate kv storage requirements, return available size * move raw erase / move into a method, allow ::set to avoid scanning storage twice * refactor naming, use in code * amend storage slicing test * fix crash handler offsets, cleanup configuration * start -> begin * eeprom readiness * dependencies * unused * SPI_FLASH constants for older Core * vtables -> templates * less include dependencies * gcov help, move estimate outside of the class * writer position can never match, use begin + offset * tweak save_crash to trigger only once in a serious crash * doh, header function should be inline * foreach api, tweak structs for public api * use test helper class * when not using foreach, move cursor reset closer to the loop using read_kv * coverage comments, fix typo in tests decltype * ensure set() does not break with offset * make codacy happy again
4 years ago
Rework settings (#2282) * wip based on early draft. todo benchmarking * fixup eraser, assume keys are unique * fix cursor copy, test removal at random * small benchmark via permutations. todo lambdas and novirtual * fix empty condition / reset * overwrite optimizations, fix move offsets overflows * ...erase using 0xff instead of 0 * test the theory with code, different length kv were bugged * try to check for out-of-bounds writes / reads * style * trying to fix mover again * clarify length, defend against reading len on edge * fix uncommited rewind change * prove space assumptions * more concise traces, fix move condition (agrh!!!) * slightly more internal knowledge (estimates API?) * make sure cursor is only valid within the range * ensure 0 does not blow things * go back up * cursor comments * comments * rewrite writes through cursor * in del too * estimate kv storage requirements, return available size * move raw erase / move into a method, allow ::set to avoid scanning storage twice * refactor naming, use in code * amend storage slicing test * fix crash handler offsets, cleanup configuration * start -> begin * eeprom readiness * dependencies * unused * SPI_FLASH constants for older Core * vtables -> templates * less include dependencies * gcov help, move estimate outside of the class * writer position can never match, use begin + offset * tweak save_crash to trigger only once in a serious crash * doh, header function should be inline * foreach api, tweak structs for public api * use test helper class * when not using foreach, move cursor reset closer to the loop using read_kv * coverage comments, fix typo in tests decltype * ensure set() does not break with offset * make codacy happy again
4 years ago
Rework settings (#2282) * wip based on early draft. todo benchmarking * fixup eraser, assume keys are unique * fix cursor copy, test removal at random * small benchmark via permutations. todo lambdas and novirtual * fix empty condition / reset * overwrite optimizations, fix move offsets overflows * ...erase using 0xff instead of 0 * test the theory with code, different length kv were bugged * try to check for out-of-bounds writes / reads * style * trying to fix mover again * clarify length, defend against reading len on edge * fix uncommited rewind change * prove space assumptions * more concise traces, fix move condition (agrh!!!) * slightly more internal knowledge (estimates API?) * make sure cursor is only valid within the range * ensure 0 does not blow things * go back up * cursor comments * comments * rewrite writes through cursor * in del too * estimate kv storage requirements, return available size * move raw erase / move into a method, allow ::set to avoid scanning storage twice * refactor naming, use in code * amend storage slicing test * fix crash handler offsets, cleanup configuration * start -> begin * eeprom readiness * dependencies * unused * SPI_FLASH constants for older Core * vtables -> templates * less include dependencies * gcov help, move estimate outside of the class * writer position can never match, use begin + offset * tweak save_crash to trigger only once in a serious crash * doh, header function should be inline * foreach api, tweak structs for public api * use test helper class * when not using foreach, move cursor reset closer to the loop using read_kv * coverage comments, fix typo in tests decltype * ensure set() does not break with offset * make codacy happy again
4 years ago
Rework settings (#2282) * wip based on early draft. todo benchmarking * fixup eraser, assume keys are unique * fix cursor copy, test removal at random * small benchmark via permutations. todo lambdas and novirtual * fix empty condition / reset * overwrite optimizations, fix move offsets overflows * ...erase using 0xff instead of 0 * test the theory with code, different length kv were bugged * try to check for out-of-bounds writes / reads * style * trying to fix mover again * clarify length, defend against reading len on edge * fix uncommited rewind change * prove space assumptions * more concise traces, fix move condition (agrh!!!) * slightly more internal knowledge (estimates API?) * make sure cursor is only valid within the range * ensure 0 does not blow things * go back up * cursor comments * comments * rewrite writes through cursor * in del too * estimate kv storage requirements, return available size * move raw erase / move into a method, allow ::set to avoid scanning storage twice * refactor naming, use in code * amend storage slicing test * fix crash handler offsets, cleanup configuration * start -> begin * eeprom readiness * dependencies * unused * SPI_FLASH constants for older Core * vtables -> templates * less include dependencies * gcov help, move estimate outside of the class * writer position can never match, use begin + offset * tweak save_crash to trigger only once in a serious crash * doh, header function should be inline * foreach api, tweak structs for public api * use test helper class * when not using foreach, move cursor reset closer to the loop using read_kv * coverage comments, fix typo in tests decltype * ensure set() does not break with offset * make codacy happy again
4 years ago
Rework settings (#2282) * wip based on early draft. todo benchmarking * fixup eraser, assume keys are unique * fix cursor copy, test removal at random * small benchmark via permutations. todo lambdas and novirtual * fix empty condition / reset * overwrite optimizations, fix move offsets overflows * ...erase using 0xff instead of 0 * test the theory with code, different length kv were bugged * try to check for out-of-bounds writes / reads * style * trying to fix mover again * clarify length, defend against reading len on edge * fix uncommited rewind change * prove space assumptions * more concise traces, fix move condition (agrh!!!) * slightly more internal knowledge (estimates API?) * make sure cursor is only valid within the range * ensure 0 does not blow things * go back up * cursor comments * comments * rewrite writes through cursor * in del too * estimate kv storage requirements, return available size * move raw erase / move into a method, allow ::set to avoid scanning storage twice * refactor naming, use in code * amend storage slicing test * fix crash handler offsets, cleanup configuration * start -> begin * eeprom readiness * dependencies * unused * SPI_FLASH constants for older Core * vtables -> templates * less include dependencies * gcov help, move estimate outside of the class * writer position can never match, use begin + offset * tweak save_crash to trigger only once in a serious crash * doh, header function should be inline * foreach api, tweak structs for public api * use test helper class * when not using foreach, move cursor reset closer to the loop using read_kv * coverage comments, fix typo in tests decltype * ensure set() does not break with offset * make codacy happy again
4 years ago
Rework settings (#2282) * wip based on early draft. todo benchmarking * fixup eraser, assume keys are unique * fix cursor copy, test removal at random * small benchmark via permutations. todo lambdas and novirtual * fix empty condition / reset * overwrite optimizations, fix move offsets overflows * ...erase using 0xff instead of 0 * test the theory with code, different length kv were bugged * try to check for out-of-bounds writes / reads * style * trying to fix mover again * clarify length, defend against reading len on edge * fix uncommited rewind change * prove space assumptions * more concise traces, fix move condition (agrh!!!) * slightly more internal knowledge (estimates API?) * make sure cursor is only valid within the range * ensure 0 does not blow things * go back up * cursor comments * comments * rewrite writes through cursor * in del too * estimate kv storage requirements, return available size * move raw erase / move into a method, allow ::set to avoid scanning storage twice * refactor naming, use in code * amend storage slicing test * fix crash handler offsets, cleanup configuration * start -> begin * eeprom readiness * dependencies * unused * SPI_FLASH constants for older Core * vtables -> templates * less include dependencies * gcov help, move estimate outside of the class * writer position can never match, use begin + offset * tweak save_crash to trigger only once in a serious crash * doh, header function should be inline * foreach api, tweak structs for public api * use test helper class * when not using foreach, move cursor reset closer to the loop using read_kv * coverage comments, fix typo in tests decltype * ensure set() does not break with offset * make codacy happy again
4 years ago
Rework settings (#2282) * wip based on early draft. todo benchmarking * fixup eraser, assume keys are unique * fix cursor copy, test removal at random * small benchmark via permutations. todo lambdas and novirtual * fix empty condition / reset * overwrite optimizations, fix move offsets overflows * ...erase using 0xff instead of 0 * test the theory with code, different length kv were bugged * try to check for out-of-bounds writes / reads * style * trying to fix mover again * clarify length, defend against reading len on edge * fix uncommited rewind change * prove space assumptions * more concise traces, fix move condition (agrh!!!) * slightly more internal knowledge (estimates API?) * make sure cursor is only valid within the range * ensure 0 does not blow things * go back up * cursor comments * comments * rewrite writes through cursor * in del too * estimate kv storage requirements, return available size * move raw erase / move into a method, allow ::set to avoid scanning storage twice * refactor naming, use in code * amend storage slicing test * fix crash handler offsets, cleanup configuration * start -> begin * eeprom readiness * dependencies * unused * SPI_FLASH constants for older Core * vtables -> templates * less include dependencies * gcov help, move estimate outside of the class * writer position can never match, use begin + offset * tweak save_crash to trigger only once in a serious crash * doh, header function should be inline * foreach api, tweak structs for public api * use test helper class * when not using foreach, move cursor reset closer to the loop using read_kv * coverage comments, fix typo in tests decltype * ensure set() does not break with offset * make codacy happy again
4 years ago
Rework settings (#2282) * wip based on early draft. todo benchmarking * fixup eraser, assume keys are unique * fix cursor copy, test removal at random * small benchmark via permutations. todo lambdas and novirtual * fix empty condition / reset * overwrite optimizations, fix move offsets overflows * ...erase using 0xff instead of 0 * test the theory with code, different length kv were bugged * try to check for out-of-bounds writes / reads * style * trying to fix mover again * clarify length, defend against reading len on edge * fix uncommited rewind change * prove space assumptions * more concise traces, fix move condition (agrh!!!) * slightly more internal knowledge (estimates API?) * make sure cursor is only valid within the range * ensure 0 does not blow things * go back up * cursor comments * comments * rewrite writes through cursor * in del too * estimate kv storage requirements, return available size * move raw erase / move into a method, allow ::set to avoid scanning storage twice * refactor naming, use in code * amend storage slicing test * fix crash handler offsets, cleanup configuration * start -> begin * eeprom readiness * dependencies * unused * SPI_FLASH constants for older Core * vtables -> templates * less include dependencies * gcov help, move estimate outside of the class * writer position can never match, use begin + offset * tweak save_crash to trigger only once in a serious crash * doh, header function should be inline * foreach api, tweak structs for public api * use test helper class * when not using foreach, move cursor reset closer to the loop using read_kv * coverage comments, fix typo in tests decltype * ensure set() does not break with offset * make codacy happy again
4 years ago
Rework settings (#2282) * wip based on early draft. todo benchmarking * fixup eraser, assume keys are unique * fix cursor copy, test removal at random * small benchmark via permutations. todo lambdas and novirtual * fix empty condition / reset * overwrite optimizations, fix move offsets overflows * ...erase using 0xff instead of 0 * test the theory with code, different length kv were bugged * try to check for out-of-bounds writes / reads * style * trying to fix mover again * clarify length, defend against reading len on edge * fix uncommited rewind change * prove space assumptions * more concise traces, fix move condition (agrh!!!) * slightly more internal knowledge (estimates API?) * make sure cursor is only valid within the range * ensure 0 does not blow things * go back up * cursor comments * comments * rewrite writes through cursor * in del too * estimate kv storage requirements, return available size * move raw erase / move into a method, allow ::set to avoid scanning storage twice * refactor naming, use in code * amend storage slicing test * fix crash handler offsets, cleanup configuration * start -> begin * eeprom readiness * dependencies * unused * SPI_FLASH constants for older Core * vtables -> templates * less include dependencies * gcov help, move estimate outside of the class * writer position can never match, use begin + offset * tweak save_crash to trigger only once in a serious crash * doh, header function should be inline * foreach api, tweak structs for public api * use test helper class * when not using foreach, move cursor reset closer to the loop using read_kv * coverage comments, fix typo in tests decltype * ensure set() does not break with offset * make codacy happy again
4 years ago
Rework settings (#2282) * wip based on early draft. todo benchmarking * fixup eraser, assume keys are unique * fix cursor copy, test removal at random * small benchmark via permutations. todo lambdas and novirtual * fix empty condition / reset * overwrite optimizations, fix move offsets overflows * ...erase using 0xff instead of 0 * test the theory with code, different length kv were bugged * try to check for out-of-bounds writes / reads * style * trying to fix mover again * clarify length, defend against reading len on edge * fix uncommited rewind change * prove space assumptions * more concise traces, fix move condition (agrh!!!) * slightly more internal knowledge (estimates API?) * make sure cursor is only valid within the range * ensure 0 does not blow things * go back up * cursor comments * comments * rewrite writes through cursor * in del too * estimate kv storage requirements, return available size * move raw erase / move into a method, allow ::set to avoid scanning storage twice * refactor naming, use in code * amend storage slicing test * fix crash handler offsets, cleanup configuration * start -> begin * eeprom readiness * dependencies * unused * SPI_FLASH constants for older Core * vtables -> templates * less include dependencies * gcov help, move estimate outside of the class * writer position can never match, use begin + offset * tweak save_crash to trigger only once in a serious crash * doh, header function should be inline * foreach api, tweak structs for public api * use test helper class * when not using foreach, move cursor reset closer to the loop using read_kv * coverage comments, fix typo in tests decltype * ensure set() does not break with offset * make codacy happy again
4 years ago
Rework settings (#2282) * wip based on early draft. todo benchmarking * fixup eraser, assume keys are unique * fix cursor copy, test removal at random * small benchmark via permutations. todo lambdas and novirtual * fix empty condition / reset * overwrite optimizations, fix move offsets overflows * ...erase using 0xff instead of 0 * test the theory with code, different length kv were bugged * try to check for out-of-bounds writes / reads * style * trying to fix mover again * clarify length, defend against reading len on edge * fix uncommited rewind change * prove space assumptions * more concise traces, fix move condition (agrh!!!) * slightly more internal knowledge (estimates API?) * make sure cursor is only valid within the range * ensure 0 does not blow things * go back up * cursor comments * comments * rewrite writes through cursor * in del too * estimate kv storage requirements, return available size * move raw erase / move into a method, allow ::set to avoid scanning storage twice * refactor naming, use in code * amend storage slicing test * fix crash handler offsets, cleanup configuration * start -> begin * eeprom readiness * dependencies * unused * SPI_FLASH constants for older Core * vtables -> templates * less include dependencies * gcov help, move estimate outside of the class * writer position can never match, use begin + offset * tweak save_crash to trigger only once in a serious crash * doh, header function should be inline * foreach api, tweak structs for public api * use test helper class * when not using foreach, move cursor reset closer to the loop using read_kv * coverage comments, fix typo in tests decltype * ensure set() does not break with offset * make codacy happy again
4 years ago
Rework settings (#2282) * wip based on early draft. todo benchmarking * fixup eraser, assume keys are unique * fix cursor copy, test removal at random * small benchmark via permutations. todo lambdas and novirtual * fix empty condition / reset * overwrite optimizations, fix move offsets overflows * ...erase using 0xff instead of 0 * test the theory with code, different length kv were bugged * try to check for out-of-bounds writes / reads * style * trying to fix mover again * clarify length, defend against reading len on edge * fix uncommited rewind change * prove space assumptions * more concise traces, fix move condition (agrh!!!) * slightly more internal knowledge (estimates API?) * make sure cursor is only valid within the range * ensure 0 does not blow things * go back up * cursor comments * comments * rewrite writes through cursor * in del too * estimate kv storage requirements, return available size * move raw erase / move into a method, allow ::set to avoid scanning storage twice * refactor naming, use in code * amend storage slicing test * fix crash handler offsets, cleanup configuration * start -> begin * eeprom readiness * dependencies * unused * SPI_FLASH constants for older Core * vtables -> templates * less include dependencies * gcov help, move estimate outside of the class * writer position can never match, use begin + offset * tweak save_crash to trigger only once in a serious crash * doh, header function should be inline * foreach api, tweak structs for public api * use test helper class * when not using foreach, move cursor reset closer to the loop using read_kv * coverage comments, fix typo in tests decltype * ensure set() does not break with offset * make codacy happy again
4 years ago
Rework settings (#2282) * wip based on early draft. todo benchmarking * fixup eraser, assume keys are unique * fix cursor copy, test removal at random * small benchmark via permutations. todo lambdas and novirtual * fix empty condition / reset * overwrite optimizations, fix move offsets overflows * ...erase using 0xff instead of 0 * test the theory with code, different length kv were bugged * try to check for out-of-bounds writes / reads * style * trying to fix mover again * clarify length, defend against reading len on edge * fix uncommited rewind change * prove space assumptions * more concise traces, fix move condition (agrh!!!) * slightly more internal knowledge (estimates API?) * make sure cursor is only valid within the range * ensure 0 does not blow things * go back up * cursor comments * comments * rewrite writes through cursor * in del too * estimate kv storage requirements, return available size * move raw erase / move into a method, allow ::set to avoid scanning storage twice * refactor naming, use in code * amend storage slicing test * fix crash handler offsets, cleanup configuration * start -> begin * eeprom readiness * dependencies * unused * SPI_FLASH constants for older Core * vtables -> templates * less include dependencies * gcov help, move estimate outside of the class * writer position can never match, use begin + offset * tweak save_crash to trigger only once in a serious crash * doh, header function should be inline * foreach api, tweak structs for public api * use test helper class * when not using foreach, move cursor reset closer to the loop using read_kv * coverage comments, fix typo in tests decltype * ensure set() does not break with offset * make codacy happy again
4 years ago
Rework settings (#2282) * wip based on early draft. todo benchmarking * fixup eraser, assume keys are unique * fix cursor copy, test removal at random * small benchmark via permutations. todo lambdas and novirtual * fix empty condition / reset * overwrite optimizations, fix move offsets overflows * ...erase using 0xff instead of 0 * test the theory with code, different length kv were bugged * try to check for out-of-bounds writes / reads * style * trying to fix mover again * clarify length, defend against reading len on edge * fix uncommited rewind change * prove space assumptions * more concise traces, fix move condition (agrh!!!) * slightly more internal knowledge (estimates API?) * make sure cursor is only valid within the range * ensure 0 does not blow things * go back up * cursor comments * comments * rewrite writes through cursor * in del too * estimate kv storage requirements, return available size * move raw erase / move into a method, allow ::set to avoid scanning storage twice * refactor naming, use in code * amend storage slicing test * fix crash handler offsets, cleanup configuration * start -> begin * eeprom readiness * dependencies * unused * SPI_FLASH constants for older Core * vtables -> templates * less include dependencies * gcov help, move estimate outside of the class * writer position can never match, use begin + offset * tweak save_crash to trigger only once in a serious crash * doh, header function should be inline * foreach api, tweak structs for public api * use test helper class * when not using foreach, move cursor reset closer to the loop using read_kv * coverage comments, fix typo in tests decltype * ensure set() does not break with offset * make codacy happy again
4 years ago
Rework settings (#2282) * wip based on early draft. todo benchmarking * fixup eraser, assume keys are unique * fix cursor copy, test removal at random * small benchmark via permutations. todo lambdas and novirtual * fix empty condition / reset * overwrite optimizations, fix move offsets overflows * ...erase using 0xff instead of 0 * test the theory with code, different length kv were bugged * try to check for out-of-bounds writes / reads * style * trying to fix mover again * clarify length, defend against reading len on edge * fix uncommited rewind change * prove space assumptions * more concise traces, fix move condition (agrh!!!) * slightly more internal knowledge (estimates API?) * make sure cursor is only valid within the range * ensure 0 does not blow things * go back up * cursor comments * comments * rewrite writes through cursor * in del too * estimate kv storage requirements, return available size * move raw erase / move into a method, allow ::set to avoid scanning storage twice * refactor naming, use in code * amend storage slicing test * fix crash handler offsets, cleanup configuration * start -> begin * eeprom readiness * dependencies * unused * SPI_FLASH constants for older Core * vtables -> templates * less include dependencies * gcov help, move estimate outside of the class * writer position can never match, use begin + offset * tweak save_crash to trigger only once in a serious crash * doh, header function should be inline * foreach api, tweak structs for public api * use test helper class * when not using foreach, move cursor reset closer to the loop using read_kv * coverage comments, fix typo in tests decltype * ensure set() does not break with offset * make codacy happy again
4 years ago
Rework settings (#2282) * wip based on early draft. todo benchmarking * fixup eraser, assume keys are unique * fix cursor copy, test removal at random * small benchmark via permutations. todo lambdas and novirtual * fix empty condition / reset * overwrite optimizations, fix move offsets overflows * ...erase using 0xff instead of 0 * test the theory with code, different length kv were bugged * try to check for out-of-bounds writes / reads * style * trying to fix mover again * clarify length, defend against reading len on edge * fix uncommited rewind change * prove space assumptions * more concise traces, fix move condition (agrh!!!) * slightly more internal knowledge (estimates API?) * make sure cursor is only valid within the range * ensure 0 does not blow things * go back up * cursor comments * comments * rewrite writes through cursor * in del too * estimate kv storage requirements, return available size * move raw erase / move into a method, allow ::set to avoid scanning storage twice * refactor naming, use in code * amend storage slicing test * fix crash handler offsets, cleanup configuration * start -> begin * eeprom readiness * dependencies * unused * SPI_FLASH constants for older Core * vtables -> templates * less include dependencies * gcov help, move estimate outside of the class * writer position can never match, use begin + offset * tweak save_crash to trigger only once in a serious crash * doh, header function should be inline * foreach api, tweak structs for public api * use test helper class * when not using foreach, move cursor reset closer to the loop using read_kv * coverage comments, fix typo in tests decltype * ensure set() does not break with offset * make codacy happy again
4 years ago
  1. /*
  2. Part of the SETTINGS MODULE
  3. Copyright (C) 2020 by Maxim Prokhorov <prokhorov dot max at outlook dot com>
  4. Reimplementation of the Embedis storage format:
  5. - https://github.com/thingSoC/embedis
  6. */
  7. #pragma once
  8. #include <Arduino.h>
  9. #include <algorithm>
  10. #include <memory>
  11. #include <vector>
  12. #include "libs/TypeChecks.h"
  13. namespace settings {
  14. namespace embedis {
  15. // 'optional' type for byte range
  16. struct ValueResult {
  17. operator bool() {
  18. return result;
  19. }
  20. bool result { false };
  21. String value;
  22. };
  23. // Sum total is calculated from:
  24. // - 4 bytes to store length of 2 values (stored as big-endian)
  25. // - N bytes of values themselves
  26. // We can't save empty keys but can save empty values as just 2 length bytes
  27. inline size_t estimate(const String& key, const String& value) {
  28. if (!key.length()) {
  29. return 0;
  30. }
  31. return (4 + key.length() + value.length());
  32. }
  33. // Note: KeyValueStore is templated to avoid having to provide RawStorageBase via virtual inheritance.
  34. template <typename RawStorageBase>
  35. class KeyValueStore {
  36. // -----------------------------------------------------------------------------------
  37. // Notice: we can only use sfinae checks with the current compiler version
  38. // TODO: provide actual benchmark comparison with 'lambda'-list-as-vtable (old Embedis style)
  39. // and vtable approach (write(), read() and commit() as pure virtual)
  40. // TODO: consider overrides for bulk operations like move (see ::del method)
  41. template <typename T>
  42. using storage_can_write_t = decltype(std::declval<T>().write(
  43. std::declval<uint16_t>(), std::declval<uint8_t>()));
  44. template <typename T>
  45. using storage_can_write = is_detected<storage_can_write_t, T>;
  46. template <typename T>
  47. using storage_can_read_t = decltype(std::declval<T>().read(std::declval<uint16_t>()));
  48. template <typename T>
  49. using storage_can_read = is_detected<storage_can_read_t, T>;
  50. template <typename T>
  51. using storage_can_commit_t = decltype(std::declval<T>().commit());
  52. template <typename T>
  53. using storage_can_commit = is_detected<storage_can_commit_t, T>;
  54. static_assert(
  55. (storage_can_commit<RawStorageBase>{} &&
  56. storage_can_read<RawStorageBase>{} &&
  57. storage_can_write<RawStorageBase>{}),
  58. "Storage class must implement read(index), write(index, byte) and commit()"
  59. );
  60. // -----------------------------------------------------------------------------------
  61. protected:
  62. // Tracking state of the parser inside of _raw_read()
  63. enum class State {
  64. Begin,
  65. End,
  66. LenByte1,
  67. LenByte2,
  68. Value
  69. };
  70. // Pointer to the region of data that we are using
  71. //
  72. // XXX: It does not matter right now, but we **will** overflow position when using sizes >= (2^16) - 1
  73. // Note: Implementation is also in the header b/c c++ won't allow us
  74. // to have a plain member (not a ptr or ref) of unknown size.
  75. // Note: There was a considiration to implement this as 'stashing iterator' to be compatible with stl algorithms.
  76. // In such implementation, we would store intermediate index and allow the user to receive a `value_proxy`,
  77. // temporary returned by `value_proxy& operator*()' that is bound to Cursor instance.
  78. // This **will** cause problems with 'reverse_iterator' or anything like it, as it expects reference to
  79. // outlive the iterator object (specifically, result of `return *--tmp`, where `tmp` is created inside of a function block)
  80. struct Cursor {
  81. Cursor(RawStorageBase& storage, uint16_t position_, uint16_t begin_, uint16_t end_) :
  82. position(position_),
  83. begin(begin_),
  84. end(end_),
  85. _storage(storage)
  86. {}
  87. Cursor(RawStorageBase& storage, uint16_t begin_, uint16_t end_) :
  88. Cursor(storage, 0, begin_, end_)
  89. {}
  90. explicit Cursor(RawStorageBase& storage) :
  91. Cursor(storage, 0, 0, 0)
  92. {}
  93. static Cursor merge(RawStorageBase& storage, const Cursor& key, const Cursor& value) {
  94. return Cursor(storage, key.begin, value.end);
  95. }
  96. static Cursor fromEnd(RawStorageBase& storage, uint16_t begin, uint16_t end) {
  97. return Cursor(storage, end - begin, begin, end);
  98. }
  99. Cursor() = delete;
  100. void reset(uint16_t begin_, uint16_t end_) {
  101. position = 0;
  102. begin = begin_;
  103. end = end_;
  104. }
  105. uint8_t read() {
  106. return _storage.read(begin + position);
  107. }
  108. void write(uint8_t value) {
  109. _storage.write(begin + position, value);
  110. }
  111. void resetBeginning() {
  112. position = 0;
  113. }
  114. void resetEnd() {
  115. position = end - begin;
  116. }
  117. size_t size() {
  118. return (end - begin);
  119. }
  120. bool inRange(uint16_t position_) {
  121. return (position_ < (end - begin));
  122. }
  123. operator bool() {
  124. return inRange(position);
  125. }
  126. uint8_t operator[](size_t position_) const {
  127. return _storage.read(begin + position_);
  128. }
  129. bool operator ==(const Cursor& other) {
  130. return (begin == other.begin) && (end == other.end);
  131. }
  132. bool operator !=(const Cursor& other) {
  133. return !(*this == other);
  134. }
  135. Cursor& operator++() {
  136. ++position;
  137. return *this;
  138. }
  139. Cursor operator++(int) {
  140. Cursor other(*this);
  141. ++*this;
  142. return other;
  143. }
  144. Cursor& operator--() {
  145. --position;
  146. return *this;
  147. }
  148. Cursor operator--(int) {
  149. Cursor other(*this);
  150. --*this;
  151. return other;
  152. }
  153. uint16_t position;
  154. uint16_t begin;
  155. uint16_t end;
  156. private:
  157. RawStorageBase& _storage;
  158. };
  159. public:
  160. // Store value location in a more reasonable forward-iterator-style manner
  161. // Allows us to skip string creation when just searching for specific values
  162. // XXX: be cautious that cursor positions **will** break when underlying storage changes
  163. struct ReadResult {
  164. friend class KeyValueStore<RawStorageBase>;
  165. ReadResult(const Cursor& cursor_) :
  166. length(0),
  167. cursor(cursor_),
  168. result(false)
  169. {}
  170. ReadResult(RawStorageBase& storage) :
  171. length(0),
  172. cursor(storage),
  173. result(false)
  174. {}
  175. operator bool() {
  176. return result;
  177. }
  178. String read() {
  179. String out;
  180. out.reserve(length);
  181. if (!length) {
  182. return out;
  183. }
  184. decltype(length) index = 0;
  185. cursor.resetBeginning();
  186. while (index < length) {
  187. out += static_cast<char>(cursor.read());
  188. ++cursor;
  189. ++index;
  190. }
  191. return out;
  192. }
  193. uint16_t length;
  194. private:
  195. Cursor cursor;
  196. bool result;
  197. };
  198. // Internal storage consists of sequences of <byte-range><length>
  199. struct KeyValueResult {
  200. operator bool() {
  201. return (key) && (value) && (key.length > 0);
  202. }
  203. bool operator !() {
  204. return !(static_cast<bool>(*this));
  205. }
  206. template <typename T = ReadResult>
  207. KeyValueResult(T&& key_, T&& value_) :
  208. key(std::forward<T>(key_)),
  209. value(std::forward<T>(value_))
  210. {}
  211. KeyValueResult(RawStorageBase& storage) :
  212. key(storage),
  213. value(storage)
  214. {}
  215. ReadResult key;
  216. ReadResult value;
  217. };
  218. // one and only possible constructor, simply move the class object into the
  219. // member variable to avoid forcing the user of the API to keep 2 objects alive.
  220. KeyValueStore(RawStorageBase&& storage, uint16_t begin, uint16_t end) :
  221. _storage(std::move(storage)),
  222. _cursor(_storage, begin, end),
  223. _state(State::Begin)
  224. {}
  225. // Try to find the matching key. Datastructure that we use does not specify
  226. // any value 'type' inside of it. We expect 'key' to be the first non-empty string,
  227. // 'value' can be empty.
  228. ValueResult get(const String& key) {
  229. return _get(key, true);
  230. }
  231. bool has(const String& key) {
  232. return static_cast<bool>(_get(key, false));
  233. }
  234. // We going be using this pattern all the time here, because we need 2 consecutive **valid** ranges
  235. // TODO: expose _read_kv() and _cursor_reset_end() so we can have 'break' here?
  236. // perhaps as a wrapper object, allow something like next() and seekBegin()
  237. template <typename CallbackType>
  238. void foreach(CallbackType callback) {
  239. _cursor_reset_end();
  240. do {
  241. auto kv = _read_kv();
  242. if (!kv) {
  243. break;
  244. }
  245. callback(std::move(kv));
  246. } while (_state != State::End);
  247. }
  248. // read every key into a vector
  249. std::vector<String> keys() {
  250. std::vector<String> out;
  251. out.reserve(count());
  252. foreach([&](KeyValueResult&& kv) {
  253. out.push_back(kv.key.read());
  254. });
  255. return out;
  256. }
  257. // set or update key with value contents. ensure 'key' isn't empty, 'value' can be empty
  258. bool set(const String& key, const String& value) {
  259. // ref. 'estimate()' implementation in regards to the storage calculation
  260. auto need = estimate(key, value);
  261. if (!need) {
  262. return false;
  263. }
  264. auto key_len = key.length();
  265. auto value_len = value.length();
  266. Cursor to_erase(_storage);
  267. bool need_erase = false;
  268. // we need the position at the 'end' of the free space
  269. auto start_pos = _cursor_reset_end();
  270. do {
  271. auto kv = _read_kv();
  272. if (!kv) {
  273. break;
  274. }
  275. start_pos = kv.value.cursor.begin;
  276. // in the very special case we can match the existing key, we either
  277. if ((kv.key.length == key_len) && (kv.key.read() == key)) {
  278. if (kv.value.length == value.length()) {
  279. // - do nothing, as the value is already set
  280. if (kv.value.read() == value) {
  281. return true;
  282. }
  283. // - overwrite the space again, with the new kv of the same length
  284. start_pos = kv.key.cursor.end;
  285. break;
  286. }
  287. // - or, erase the existing kv and place new kv at the end
  288. to_erase.reset(kv.value.cursor.begin, kv.key.cursor.end);
  289. need_erase = true;
  290. }
  291. } while (_state != State::End);
  292. if (need_erase) {
  293. _raw_erase(start_pos, to_erase);
  294. start_pos += to_erase.size();
  295. }
  296. // we should only insert when possition is still within possible size
  297. if (start_pos && (start_pos >= need)) {
  298. auto writer = Cursor::fromEnd(_storage, start_pos - need, start_pos);
  299. // put the length of the value as 2 bytes and then write the data
  300. (--writer).write(key_len & 0xff);
  301. (--writer).write((key_len >> 8) & 0xff);
  302. while (key_len--) {
  303. (--writer).write(key[key_len]);
  304. }
  305. (--writer).write(value_len & 0xff);
  306. (--writer).write((value_len >> 8) & 0xff);
  307. while (value_len--) {
  308. (--writer).write(value[value_len]);
  309. }
  310. // we also need to add an empty key *after* the value
  311. // but, only when we still have some space left
  312. if (writer.begin >= 2) {
  313. _cursor_set_position(writer.begin - _cursor.begin);
  314. auto next_kv = _read_kv();
  315. if (!next_kv) {
  316. auto empty = Cursor::fromEnd(_storage, writer.begin - 2, writer.begin);
  317. (--empty).write(0);
  318. (--empty).write(0);
  319. }
  320. }
  321. _storage.commit();
  322. return true;
  323. }
  324. return false;
  325. }
  326. // remove key from the storage. will check that 'key' argument isn't empty
  327. bool del(const String& key) {
  328. size_t key_len = key.length();
  329. if (!key_len) {
  330. return false;
  331. }
  332. // we should only compare strings of equal length.
  333. // when matching, record { value ... key } range + 4 bytes for length
  334. // continue searching for available keys and set start_pos and the 'end' of the free space
  335. size_t start_pos = _cursor_reset_end();
  336. auto to_erase = Cursor::fromEnd(_storage, _cursor.begin, _cursor.end);
  337. foreach([&](KeyValueResult&& kv) {
  338. start_pos = kv.value.cursor.begin;
  339. if (!to_erase && (kv.key.length == key_len) && (kv.key.read() == key)) {
  340. to_erase.reset(kv.value.cursor.begin, kv.key.cursor.end);
  341. }
  342. });
  343. if (to_erase) {
  344. _raw_erase(start_pos, to_erase);
  345. return true;
  346. }
  347. return false;
  348. }
  349. // Simply count key-value pairs that we could parse
  350. size_t count() {
  351. size_t result = 0;
  352. foreach([&result](KeyValueResult&&) {
  353. ++result;
  354. });
  355. return result;
  356. }
  357. // Do exactly the same thing as 'keys' does, but return the amount
  358. // of bytes to the left of the last kv
  359. size_t available() {
  360. size_t result = _cursor.size();
  361. foreach([&result](KeyValueResult&& kv) {
  362. result -= kv.key.cursor.size();
  363. result -= kv.value.cursor.size();
  364. });
  365. return result;
  366. }
  367. // How much bytes can be used is directly read from the cursor, based on begin and end values
  368. size_t size() {
  369. return _cursor.size();
  370. }
  371. protected:
  372. // Try to find the matching key. Datastructure that we use does not specify
  373. // any value 'type' inside of it. We expect 'key' to be the first non-empty string,
  374. // 'value' can be empty.
  375. // To implement has(), allow to skip reading the value
  376. ValueResult _get(const String& key, bool read_value) {
  377. ValueResult out;
  378. auto len = key.length();
  379. _cursor_reset_end();
  380. do {
  381. auto kv = _read_kv();
  382. if (!kv) {
  383. break;
  384. }
  385. // no point in comparing keys when length does not match
  386. // (and we also don't want to allocate the string)
  387. if (kv.key.length != len) {
  388. continue;
  389. }
  390. auto key_result = kv.key.read();
  391. if (key_result == key) {
  392. if (read_value) {
  393. out.value = kv.value.read();
  394. }
  395. out.result = true;
  396. break;
  397. }
  398. } while (_state != State::End);
  399. return out;
  400. }
  401. // Place cursor at the `end` and resets the parser to expect length byte
  402. uint16_t _cursor_reset_end() {
  403. _cursor.resetEnd();
  404. _state = State::Begin;
  405. return _cursor.end;
  406. }
  407. uint16_t _cursor_set_position(uint16_t position) {
  408. _state = State::Begin;
  409. _cursor.position = position;
  410. return _cursor.position;
  411. }
  412. // implementation quirk is that `Cursor::operator=` won't work because of the `RawStorageBase&` member
  413. // right now, just construct in place and assume that compiler will inline things
  414. KeyValueResult _read_kv() {
  415. auto key = _raw_read();
  416. if (!key || !key.length) {
  417. return {_storage};
  418. }
  419. auto value = _raw_read();
  420. return {key, value};
  421. };
  422. void _raw_erase(size_t start_pos, Cursor& to_erase) {
  423. // we either end up to the left or to the right of the boundary
  424. size_t new_pos = (start_pos < to_erase.begin)
  425. ? (start_pos + to_erase.size())
  426. : (to_erase.end);
  427. if (start_pos < to_erase.begin) {
  428. // shift storage to the right, overwriting over the now empty space
  429. auto from = Cursor::fromEnd(_storage, start_pos, to_erase.begin);
  430. auto to = Cursor::fromEnd(_storage, start_pos + to_erase.size(), to_erase.end);
  431. while (--from && --to) {
  432. to.write(from.read());
  433. from.write(0xff);
  434. }
  435. } else {
  436. // overwrite the now empty space with 0xff
  437. to_erase.resetEnd();
  438. while (--to_erase) {
  439. to_erase.write(0xff);
  440. }
  441. }
  442. // same as set(), add empty key as padding
  443. auto empty = Cursor::fromEnd(_storage, new_pos - 2, new_pos);
  444. (--empty).write(0);
  445. (--empty).write(0);
  446. _storage.commit();
  447. }
  448. // Returns Cursor to the region that holds the data
  449. // Result object itself does not contain any data, we need to explicitly request it by calling read()
  450. //
  451. // Cursor object is always expected to point to something, e.g. minimum:
  452. // 0x00 0x00
  453. // len2 len1
  454. // Position will be 0, end will be 2. Total length is 2, data length is 0
  455. //
  456. // Or, non-empty value:
  457. // 0x01 0x00 0x01
  458. // data len2 len1
  459. // Position will be 0, end will be 3. Total length is 3, data length is 1
  460. ReadResult _raw_read() {
  461. uint16_t len = 0;
  462. ReadResult out(_storage);
  463. do {
  464. // storage is written right-to-left, cursor is always decreasing
  465. switch (_state) {
  466. case State::Begin:
  467. if (_cursor.position >= 2) {
  468. --_cursor;
  469. _state = State::LenByte1;
  470. } else {
  471. _state = State::End;
  472. }
  473. break;
  474. // len is 16 bit uint (bigendian)
  475. // special case is 0, which is valid and should be returned when encountered
  476. // another special case is 0xffff, meaning we just hit an empty space
  477. case State::LenByte1:
  478. len = _cursor.read();
  479. _state = State::LenByte2;
  480. break;
  481. case State::LenByte2:
  482. {
  483. uint8_t len2 = (--_cursor).read();
  484. if ((0xff == len) && (0xff == len2)) {
  485. _state = State::End;
  486. } else {
  487. len |= len2 << 8;
  488. _state = State::Value;
  489. }
  490. break;
  491. }
  492. case State::Value: {
  493. // ensure we don't go out-of-bounds
  494. if (len && _cursor.position < len) {
  495. _state = State::End;
  496. break;
  497. }
  498. // and point at the beginning of the value
  499. _cursor.position -= len;
  500. auto value_start = (_cursor.begin + _cursor.position);
  501. out.cursor.reset(value_start, value_start + len + 2);
  502. out.length = len;
  503. out.result = true;
  504. _state = State::Begin;
  505. goto return_result;
  506. }
  507. case State::End:
  508. default:
  509. break;
  510. }
  511. } while (_state != State::End);
  512. return_result:
  513. return out;
  514. }
  515. RawStorageBase _storage;
  516. Cursor _cursor;
  517. State _state { State::Begin };
  518. };
  519. } // namespace embedis
  520. } // namespace settings