| 766 | } |
| 767 | |
| 768 | bool CssParser::loadFromCache() { |
| 769 | if (cachePath.empty()) { |
| 770 | return false; |
| 771 | } |
| 772 | |
| 773 | HalFile file; |
| 774 | if (!Storage.openFileForRead("CSS", cachePath + rulesCache, file)) { |
| 775 | return false; |
| 776 | } |
| 777 | |
| 778 | // Clear existing rules |
| 779 | clear(); |
| 780 | |
| 781 | // Read and verify version |
| 782 | uint8_t version = 0; |
| 783 | if (file.read(&version, 1) != 1 || version != CssParser::CSS_CACHE_VERSION) { |
| 784 | LOG_DBG("CSS", "Cache version mismatch (got %u, expected %u), removing stale cache for rebuild", version, |
| 785 | CssParser::CSS_CACHE_VERSION); |
| 786 | // Explicitly close() file before calling Storage.remove() |
| 787 | file.close(); |
| 788 | Storage.remove((cachePath + rulesCache).c_str()); |
| 789 | return false; |
| 790 | } |
| 791 | |
| 792 | // Read rule count |
| 793 | uint16_t ruleCount = 0; |
| 794 | if (file.read(&ruleCount, sizeof(ruleCount)) != sizeof(ruleCount)) { |
| 795 | return false; |
| 796 | } |
| 797 | |
| 798 | if (ruleCount > MAX_RULES) { |
| 799 | LOG_DBG("CSS", "Invalid cache rule count (%u > %zu)", ruleCount, MAX_RULES); |
| 800 | rulesBySelector_.clear(); |
| 801 | return false; |
| 802 | } |
| 803 | |
| 804 | auto hasRemainingBytes = [&file](const size_t neededBytes) -> bool { |
| 805 | return static_cast<size_t>(file.available()) >= neededBytes; |
| 806 | }; |
| 807 | |
| 808 | constexpr size_t CSS_LENGTH_FIELD_COUNT = 11; |
| 809 | constexpr size_t CSS_LENGTH_BYTES = sizeof(float) + sizeof(uint8_t); |
| 810 | constexpr size_t CSS_FIXED_STYLE_BYTES = |
| 811 | 5 * sizeof(uint8_t) + (CSS_LENGTH_FIELD_COUNT * CSS_LENGTH_BYTES) + sizeof(uint8_t) + sizeof(uint32_t); |
| 812 | |
| 813 | // Read each rule |
| 814 | for (uint16_t i = 0; i < ruleCount; ++i) { |
| 815 | // Read selector string |
| 816 | uint16_t selectorLen = 0; |
| 817 | if (!hasRemainingBytes(sizeof(selectorLen))) { |
| 818 | rulesBySelector_.clear(); |
| 819 | return false; |
| 820 | } |
| 821 | if (file.read(&selectorLen, sizeof(selectorLen)) != sizeof(selectorLen)) { |
| 822 | rulesBySelector_.clear(); |
| 823 | return false; |
| 824 | } |
| 825 | |