Use generics instead of dynamic dispatch

Currently in the `bip158` module we do a bunch of dynamic dispatch using
the reader/writer objects. We can improve runtime performance, at the
cost of compile time, by using generics instead of dynamic dispatch.
This commit is contained in:
Tobin C. Harding 2022-08-08 16:24:25 +10:00
parent d79c6b8358
commit 28853fd3cc
1 changed files with 52 additions and 24 deletions

View File

@ -137,27 +137,33 @@ impl BlockFilter {
} }
/// match any query pattern /// match any query pattern
pub fn match_any(&self, block_hash: &BlockHash, query: &mut dyn Iterator<Item=&[u8]>) -> Result<bool, Error> { pub fn match_any<'a, I>(&self, block_hash: &BlockHash, query: I) -> Result<bool, Error>
where
I: Iterator<Item = &'a [u8]>,
{
let filter_reader = BlockFilterReader::new(block_hash); let filter_reader = BlockFilterReader::new(block_hash);
filter_reader.match_any(&mut self.content.as_slice(), query) filter_reader.match_any(&mut self.content.as_slice(), query)
} }
/// match all query pattern /// match all query pattern
pub fn match_all(&self, block_hash: &BlockHash, query: &mut dyn Iterator<Item=&[u8]>) -> Result<bool, Error> { pub fn match_all<'a, I>(&self, block_hash: &BlockHash, query: I) -> Result<bool, Error>
where
I: Iterator<Item = &'a [u8]>,
{
let filter_reader = BlockFilterReader::new(block_hash); let filter_reader = BlockFilterReader::new(block_hash);
filter_reader.match_all(&mut self.content.as_slice(), query) filter_reader.match_all(&mut self.content.as_slice(), query)
} }
} }
/// Compiles and writes a block filter /// Compiles and writes a block filter
pub struct BlockFilterWriter<'a> { pub struct BlockFilterWriter<'a, W> {
block: &'a Block, block: &'a Block,
writer: GCSFilterWriter<'a>, writer: GCSFilterWriter<'a, W>,
} }
impl<'a> BlockFilterWriter<'a> { impl<'a, W: io::Write> BlockFilterWriter<'a, W> {
/// Create a block filter writer /// Create a block filter writer
pub fn new(writer: &'a mut dyn io::Write, block: &'a Block) -> BlockFilterWriter<'a> { pub fn new(writer: &'a mut W, block: &'a Block) -> BlockFilterWriter<'a, W> {
let block_hash_as_int = block.block_hash().into_inner(); let block_hash_as_int = block.block_hash().into_inner();
let k0 = endian::slice_to_u64_le(&block_hash_as_int[0..8]); let k0 = endian::slice_to_u64_le(&block_hash_as_int[0..8]);
let k1 = endian::slice_to_u64_le(&block_hash_as_int[8..16]); let k1 = endian::slice_to_u64_le(&block_hash_as_int[8..16]);
@ -218,12 +224,20 @@ impl BlockFilterReader {
} }
/// match any query pattern /// match any query pattern
pub fn match_any(&self, reader: &mut dyn io::Read, query: &mut dyn Iterator<Item=&[u8]>) -> Result<bool, Error> { pub fn match_any<'a, I, R>(&self, reader: &mut R, query: I) -> Result<bool, Error>
where
I: Iterator<Item = &'a [u8]>,
R: io::Read + ?Sized,
{
self.reader.match_any(reader, query) self.reader.match_any(reader, query)
} }
/// match all query pattern /// match all query pattern
pub fn match_all(&self, reader: &mut dyn io::Read, query: &mut dyn Iterator<Item=&[u8]>) -> Result<bool, Error> { pub fn match_all<'a, I, R>(&self, reader: &mut R, query: I) -> Result<bool, Error>
where
I: Iterator<Item = &'a [u8]>,
R: io::Read + ?Sized,
{
self.reader.match_all(reader, query) self.reader.match_all(reader, query)
} }
} }
@ -242,7 +256,11 @@ impl GCSFilterReader {
} }
/// match any query pattern /// match any query pattern
pub fn match_any(&self, reader: &mut dyn io::Read, query: &mut dyn Iterator<Item=&[u8]>) -> Result<bool, Error> { pub fn match_any<'a, I, R>(&self, reader: &mut R, query: I) -> Result<bool, Error>
where
I: Iterator<Item = &'a [u8]>,
R: io::Read + ?Sized,
{
let mut decoder = reader; let mut decoder = reader;
let n_elements: VarInt = Decodable::consensus_decode(&mut decoder).unwrap_or(VarInt(0)); let n_elements: VarInt = Decodable::consensus_decode(&mut decoder).unwrap_or(VarInt(0));
let reader = &mut decoder; let reader = &mut decoder;
@ -282,7 +300,11 @@ impl GCSFilterReader {
} }
/// match all query pattern /// match all query pattern
pub fn match_all(&self, reader: &mut dyn io::Read, query: &mut dyn Iterator<Item=&[u8]>) -> Result<bool, Error> { pub fn match_all<'a, I, R>(&self, reader: &mut R, query: I) -> Result<bool, Error>
where
I: Iterator<Item = &'a [u8]>,
R: io::Read + ?Sized,
{
let mut decoder = reader; let mut decoder = reader;
let n_elements: VarInt = Decodable::consensus_decode(&mut decoder).unwrap_or(VarInt(0)); let n_elements: VarInt = Decodable::consensus_decode(&mut decoder).unwrap_or(VarInt(0));
let reader = &mut decoder; let reader = &mut decoder;
@ -329,16 +351,16 @@ fn map_to_range(hash: u64, nm: u64) -> u64 {
} }
/// Colomb-Rice encoded filter writer /// Colomb-Rice encoded filter writer
pub struct GCSFilterWriter<'a> { pub struct GCSFilterWriter<'a, W> {
filter: GCSFilter, filter: GCSFilter,
writer: &'a mut dyn io::Write, writer: &'a mut W,
elements: HashSet<Vec<u8>>, elements: HashSet<Vec<u8>>,
m: u64 m: u64
} }
impl<'a> GCSFilterWriter<'a> { impl<'a, W: io::Write> GCSFilterWriter<'a, W> {
/// Create a new GCS writer wrapping a generic writer, with specific seed to siphash /// Create a new GCS writer wrapping a generic writer, with specific seed to siphash
pub fn new(writer: &'a mut dyn io::Write, k0: u64, k1: u64, m: u64, p: u8) -> GCSFilterWriter<'a> { pub fn new(writer: &'a mut W, k0: u64, k1: u64, m: u64, p: u8) -> GCSFilterWriter<'a, W> {
GCSFilterWriter { GCSFilterWriter {
filter: GCSFilter::new(k0, k1, p), filter: GCSFilter::new(k0, k1, p),
writer, writer,
@ -392,7 +414,10 @@ impl GCSFilter {
} }
/// Golomb-Rice encode a number n to a bit stream (Parameter 2^k) /// Golomb-Rice encode a number n to a bit stream (Parameter 2^k)
fn golomb_rice_encode(&self, writer: &mut BitStreamWriter, n: u64) -> Result<usize, io::Error> { fn golomb_rice_encode<'a, W>(&self, writer: &mut BitStreamWriter<'a, W>, n: u64) -> Result<usize, io::Error>
where
W: io::Write,
{
let mut wrote = 0; let mut wrote = 0;
let mut q = n >> self.p; let mut q = n >> self.p;
while q > 0 { while q > 0 {
@ -406,7 +431,10 @@ impl GCSFilter {
} }
/// Golomb-Rice decode a number from a bit stream (Parameter 2^k) /// Golomb-Rice decode a number from a bit stream (Parameter 2^k)
fn golomb_rice_decode(&self, reader: &mut BitStreamReader) -> Result<u64, io::Error> { fn golomb_rice_decode<R>(&self, reader: &mut BitStreamReader<R>) -> Result<u64, io::Error>
where
R: io::Read
{
let mut q = 0u64; let mut q = 0u64;
while reader.read(1)? == 1 { while reader.read(1)? == 1 {
q += 1; q += 1;
@ -422,15 +450,15 @@ impl GCSFilter {
} }
/// Bitwise stream reader /// Bitwise stream reader
pub struct BitStreamReader<'a> { pub struct BitStreamReader<'a, R> {
buffer: [u8; 1], buffer: [u8; 1],
offset: u8, offset: u8,
reader: &'a mut dyn io::Read, reader: &'a mut R,
} }
impl<'a> BitStreamReader<'a> { impl<'a, R: io::Read> BitStreamReader<'a, R> {
/// Create a new BitStreamReader that reads bitwise from a given reader /// Create a new BitStreamReader that reads bitwise from a given reader
pub fn new(reader: &'a mut dyn io::Read) -> BitStreamReader { pub fn new(reader: &'a mut R) -> BitStreamReader<'a, R> {
BitStreamReader { BitStreamReader {
buffer: [0u8], buffer: [0u8],
reader, reader,
@ -460,15 +488,15 @@ impl<'a> BitStreamReader<'a> {
} }
/// Bitwise stream writer /// Bitwise stream writer
pub struct BitStreamWriter<'a> { pub struct BitStreamWriter<'a, W> {
buffer: [u8; 1], buffer: [u8; 1],
offset: u8, offset: u8,
writer: &'a mut dyn io::Write, writer: &'a mut W,
} }
impl<'a> BitStreamWriter<'a> { impl<'a, W: io::Write> BitStreamWriter<'a, W> {
/// Create a new BitStreamWriter that writes bitwise to a given writer /// Create a new BitStreamWriter that writes bitwise to a given writer
pub fn new(writer: &'a mut dyn io::Write) -> BitStreamWriter { pub fn new(writer: &'a mut W) -> BitStreamWriter<'a, W> {
BitStreamWriter { BitStreamWriter {
buffer: [0u8], buffer: [0u8],
writer, writer,