A fork of https://github.com/crosspoint-reader/crosspoint-reader
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

refactor: Use std binary search algorithms for font lookups (#1202)

## Summary

**What is the goal of this PR?**

Rewrite of font routines to use std binary search algorithms instead of
custom repeated implementations: `lookupKernClass`,
`EpdFont::getLigature`, and `EpdFont::getGlyph`.

---

### AI Usage

While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.

Did you use AI tools to help write this code? _**NO**_

authored by

Zach Nelson and committed by
GitHub
3cc8e272 80d18563

+32 -42
+32 -42
lib/EpdFont/EpdFont.cpp
··· 80 80 if (!entries || count == 0 || cp > 0xFFFF) { 81 81 return 0; 82 82 } 83 + 83 84 const auto target = static_cast<uint16_t>(cp); 84 - int left = 0; 85 - int right = static_cast<int>(count) - 1; 86 - while (left <= right) { 87 - const int mid = left + (right - left) / 2; 88 - const uint16_t midCp = entries[mid].codepoint; 89 - if (midCp == target) { 90 - return entries[mid].classId; 91 - } 92 - if (midCp < target) { 93 - left = mid + 1; 94 - } else { 95 - right = mid - 1; 96 - } 85 + const auto* end = entries + count; 86 + 87 + // lower_bound: exact-key lookup. Finds the first entry with codepoint >= target, 88 + // then the equality check confirms an exact match exists. 89 + const auto it = std::lower_bound( 90 + entries, end, target, [](const EpdKernClassEntry& entry, uint16_t value) { return entry.codepoint < value; }); 91 + 92 + if (it != end && it->codepoint == target) { 93 + return it->classId; 97 94 } 95 + 98 96 return 0; 99 97 } 100 98 ··· 117 115 } 118 116 119 117 const uint32_t key = (leftCp << 16) | rightCp; 120 - int left = 0; 121 - int right = static_cast<int>(count) - 1; 118 + const auto* end = pairs + count; 122 119 123 - while (left <= right) { 124 - const int mid = left + (right - left) / 2; 125 - const uint32_t midKey = pairs[mid].pair; 126 - if (midKey == key) { 127 - return pairs[mid].ligatureCp; 128 - } 129 - if (midKey < key) { 130 - left = mid + 1; 131 - } else { 132 - right = mid - 1; 133 - } 120 + // lower_bound: exact-key lookup. Finds the first entry with pair >= key, 121 + // then the equality check confirms an exact match exists. 122 + const auto it = 123 + std::lower_bound(pairs, end, key, [](const EpdLigaturePair& pair, uint32_t value) { return pair.pair < value; }); 124 + 125 + if (it != end && it->pair == key) { 126 + return it->ligatureCp; 134 127 } 128 + 135 129 return 0; 136 130 } 137 131 ··· 154 148 } 155 149 156 150 const EpdGlyph* EpdFont::getGlyph(const uint32_t cp) const { 157 - const EpdUnicodeInterval* intervals = data->intervals; 158 151 const int count = data->intervalCount; 159 - 160 152 if (count == 0) return nullptr; 161 153 162 - // Binary search for O(log n) lookup instead of O(n) 163 - // Critical for Korean fonts with many unicode intervals 164 - int left = 0; 165 - int right = count - 1; 154 + const EpdUnicodeInterval* intervals = data->intervals; 155 + const auto* end = intervals + count; 166 156 167 - while (left <= right) { 168 - const int mid = left + (right - left) / 2; 169 - const EpdUnicodeInterval* interval = &intervals[mid]; 157 + // upper_bound: range lookup. Finds the first interval with first > cp, so the 158 + // interval just before it is the last one with first <= cp. That's the only 159 + // candidate that could contain cp. Then we verify cp <= candidate.last. 160 + const auto it = std::upper_bound( 161 + intervals, end, cp, [](uint32_t value, const EpdUnicodeInterval& interval) { return value < interval.first; }); 170 162 171 - if (cp < interval->first) { 172 - right = mid - 1; 173 - } else if (cp > interval->last) { 174 - left = mid + 1; 175 - } else { 176 - // Found: cp >= interval->first && cp <= interval->last 177 - return &data->glyph[interval->offset + (cp - interval->first)]; 163 + if (it != intervals) { 164 + const auto& interval = *(it - 1); 165 + if (cp <= interval.last) { 166 + return &data->glyph[interval.offset + (cp - interval.first)]; 178 167 } 179 168 } 169 + 180 170 if (cp != REPLACEMENT_GLYPH) { 181 171 return getGlyph(REPLACEMENT_GLYPH); 182 172 }