avoid lowercase allocations during asset registry search

This commit is contained in:
mononaut 2026-05-29 09:13:53 +00:00
parent 237a2df61e
commit f11f6ab09a
No known key found for this signature in database
GPG Key ID: BFD16BE592A9CD8D

View File

@ -56,7 +56,7 @@ impl AssetRegistry {
}
pub fn search(&self, query: &str, limit: usize) -> Vec<AssetEntry<'_>> {
let query = query.trim().to_lowercase();
let query = query.trim();
if query.is_empty() || limit == 0 {
return vec![];
}
@ -65,21 +65,21 @@ impl AssetRegistry {
self.assets_cache
.iter()
.map(|(asset_id, (_, metadata))| (asset_id, metadata)),
&query,
query,
limit,
|metadata| metadata.ticker.as_deref(),
);
if results.len() < limit {
let (name_matches, candidates) =
search_by(candidates, &query, limit - results.len(), |metadata| {
search_by(candidates, query, limit - results.len(), |metadata| {
Some(&metadata.name)
});
results.extend(name_matches);
if results.len() < limit {
let (domain_matches, _) =
search_by(candidates, &query, limit - results.len(), AssetMeta::domain);
search_by(candidates, query, limit - results.len(), AssetMeta::domain);
results.extend(domain_matches);
}
}
@ -243,8 +243,8 @@ where
for (asset_id, metadata) in candidates {
let position = field(metadata).and_then(|field| {
let lc_field = field.to_lowercase();
lc_field.find(query).map(|position| (position, lc_field))
// registry fields are ascii, so we don't need full unicode case-folding
ascii_ci_find(field, query).map(|position| (position, field))
});
if let Some((position, field)) = position {
@ -259,7 +259,7 @@ where
matches.sort_unstable_by(|a, b| {
a.0.cmp(&b.0)
.then_with(|| a.1.cmp(&b.1))
.then_with(|| ascii_ci_cmp(a.1, b.1))
.then_with(|| a.2.cmp(b.2))
});
@ -272,3 +272,27 @@ where
remaining,
)
}
// zero-allocation case-insensitive ASCII substring search
// returns the byte offset of the first match
fn ascii_ci_find(haystack: &str, needle: &str) -> Option<usize> {
let (haystack, needle) = (haystack.as_bytes(), needle.as_bytes());
if needle.is_empty() {
return Some(0);
}
haystack
.windows(needle.len())
.position(|window| window.eq_ignore_ascii_case(needle))
}
// zero-allocation case-insensitive ASCII string comparison
fn ascii_ci_cmp(a: &str, b: &str) -> cmp::Ordering {
let (a, b) = (a.as_bytes(), b.as_bytes());
for i in 0..a.len().min(b.len()) {
match a[i].to_ascii_lowercase().cmp(&b[i].to_ascii_lowercase()) {
cmp::Ordering::Equal => continue,
ord => return ord,
}
}
a.len().cmp(&b.len())
}