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.

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