use brk_error::{Error, OptionData, Result};
use brk_types::{Addr, AddrHash, AddrHashPrefixMatches, OutputType};
use crate::Query;
const ADDR_HASH_PREFIX_MATCH_LIMIT: usize = 100;
impl Query {
pub fn addr_hash_prefix_matches(
&self,
addr_type: OutputType,
prefix: &str,
) -> Result<AddrHashPrefixMatches> {
if !addr_type.is_addr() {
return Err(Error::UnsupportedType(addr_type.to_string()));
}
let prefix = AddrHashPrefix::parse(prefix)?;
let store = self
.indexer()
.stores
.addr_type_to_addr_hash_to_addr_index
.get(addr_type)
.data()?;
let safe_type_index = self.safe_lengths().to_type_index(addr_type);
let addr_readers = self.indexer().vecs.addrs.addr_readers();
let mut addresses = Vec::new();
let max_hash = AddrHash::new(u64::MAX);
if let Some(upper) = prefix.upper {
for (_, type_index) in store.range(prefix.lower..upper) {
if type_index >= safe_type_index {
continue;
}
let script = addr_readers.script_pubkey(addr_type, type_index);
addresses.push(Addr::try_from((&script, addr_type))?);
if addresses.len() > ADDR_HASH_PREFIX_MATCH_LIMIT {
break;
}
}
} else {
for (_, type_index) in store.range(prefix.lower..max_hash) {
if type_index >= safe_type_index {
continue;
}
let script = addr_readers.script_pubkey(addr_type, type_index);
addresses.push(Addr::try_from((&script, addr_type))?);
if addresses.len() > ADDR_HASH_PREFIX_MATCH_LIMIT {
break;
}
}
if addresses.len() <= ADDR_HASH_PREFIX_MATCH_LIMIT
&& let Some(type_index) = store.get(&max_hash)?.map(|cow| cow.into_owned())
&& type_index < safe_type_index
{
let script = addr_readers.script_pubkey(addr_type, type_index);
addresses.push(Addr::try_from((&script, addr_type))?);
}
}
let truncated = addresses.len() > ADDR_HASH_PREFIX_MATCH_LIMIT;
addresses.truncate(ADDR_HASH_PREFIX_MATCH_LIMIT);
Ok(AddrHashPrefixMatches {
addr_type,
prefix: prefix.text,
truncated,
addresses,
})
}
}
struct AddrHashPrefix {
text: String,
lower: AddrHash,
upper: Option<AddrHash>,
}
impl AddrHashPrefix {
const MAX_NIBBLES: usize = u64::BITS as usize / 4;
fn parse(prefix: &str) -> Result<Self> {
let nibbles = prefix.len();
if !(1..=Self::MAX_NIBBLES).contains(&nibbles) {
return Err(Self::parse_error());
}
let value = u64::from_str_radix(prefix, 16).map_err(|_| Self::parse_error())?;
let shift = (Self::MAX_NIBBLES - nibbles) * 4;
let factor = 1_u64 << shift;
let lower = value * factor;
let upper = value
.checked_add(1)
.and_then(|value| value.checked_mul(factor))
.map(AddrHash::new);
Ok(Self {
text: prefix.to_ascii_lowercase(),
lower: AddrHash::new(lower),
upper,
})
}
fn parse_error() -> Error {
Error::Parse(format!(
"hash prefix must be 1 to {} hexadecimal characters",
Self::MAX_NIBBLES
))
}
}