mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-04-27 16:19:59 -07:00
computer: snapshot
This commit is contained in:
@@ -128,7 +128,7 @@ pub(crate) fn process_blocks(
|
||||
debug!("txindex_to_height RangeMap built");
|
||||
|
||||
// Create reusable iterators for sequential txout/txin reads (16KB buffered)
|
||||
let txout_iters = TxOutReaders::new(indexer);
|
||||
let mut txout_iters = TxOutReaders::new(indexer);
|
||||
let mut txin_iters = TxInReaders::new(indexer, inputs, &mut txindex_to_height);
|
||||
|
||||
// Pre-collect first address indexes per type for the block range
|
||||
|
||||
@@ -21,32 +21,40 @@ pub struct TxOutData {
|
||||
pub typeindex: TypeIndex,
|
||||
}
|
||||
|
||||
/// Readers for txout vectors. Uses collect_range for bulk reads.
|
||||
/// Readers for txout vectors. Reuses internal buffers across blocks.
|
||||
pub struct TxOutReaders<'a> {
|
||||
indexer: &'a Indexer,
|
||||
values_buf: Vec<Sats>,
|
||||
outputtypes_buf: Vec<OutputType>,
|
||||
typeindexes_buf: Vec<TypeIndex>,
|
||||
}
|
||||
|
||||
impl<'a> TxOutReaders<'a> {
|
||||
pub(crate) fn new(indexer: &'a Indexer) -> Self {
|
||||
Self { indexer }
|
||||
Self {
|
||||
indexer,
|
||||
values_buf: Vec::new(),
|
||||
outputtypes_buf: Vec::new(),
|
||||
typeindexes_buf: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Collect output data for a block range using bulk reads.
|
||||
/// Collect output data for a block range using bulk reads with buffer reuse.
|
||||
pub(crate) fn collect_block_outputs(
|
||||
&self,
|
||||
&mut self,
|
||||
first_txoutindex: usize,
|
||||
output_count: usize,
|
||||
) -> Vec<TxOutData> {
|
||||
let end = first_txoutindex + output_count;
|
||||
let values: Vec<Sats> = self.indexer.vecs.outputs.value.collect_range_at(first_txoutindex, end);
|
||||
let outputtypes: Vec<OutputType> = self.indexer.vecs.outputs.outputtype.collect_range_at(first_txoutindex, end);
|
||||
let typeindexes: Vec<TypeIndex> = self.indexer.vecs.outputs.typeindex.collect_range_at(first_txoutindex, end);
|
||||
self.indexer.vecs.outputs.value.collect_range_into_at(first_txoutindex, end, &mut self.values_buf);
|
||||
self.indexer.vecs.outputs.outputtype.collect_range_into_at(first_txoutindex, end, &mut self.outputtypes_buf);
|
||||
self.indexer.vecs.outputs.typeindex.collect_range_into_at(first_txoutindex, end, &mut self.typeindexes_buf);
|
||||
|
||||
values
|
||||
.into_iter()
|
||||
.zip(outputtypes)
|
||||
.zip(typeindexes)
|
||||
.map(|((value, outputtype), typeindex)| TxOutData {
|
||||
self.values_buf
|
||||
.iter()
|
||||
.zip(&self.outputtypes_buf)
|
||||
.zip(&self.typeindexes_buf)
|
||||
.map(|((&value, &outputtype), &typeindex)| TxOutData {
|
||||
value,
|
||||
outputtype,
|
||||
typeindex,
|
||||
@@ -55,11 +63,12 @@ impl<'a> TxOutReaders<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Readers for txin vectors. Uses collect_range for bulk reads.
|
||||
/// Readers for txin vectors. Reuses outpoint buffer across blocks.
|
||||
pub struct TxInReaders<'a> {
|
||||
indexer: &'a Indexer,
|
||||
txins: &'a inputs::Vecs,
|
||||
txindex_to_height: &'a mut RangeMap<TxIndex, Height>,
|
||||
outpoints_buf: Vec<OutPoint>,
|
||||
}
|
||||
|
||||
impl<'a> TxInReaders<'a> {
|
||||
@@ -72,11 +81,12 @@ impl<'a> TxInReaders<'a> {
|
||||
indexer,
|
||||
txins,
|
||||
txindex_to_height,
|
||||
outpoints_buf: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Collect input data for a block range using bulk reads.
|
||||
/// Computes prev_height on-the-fly from outpoint using RangeMap lookup.
|
||||
/// Outpoint buffer is reused across blocks; returned vecs are fresh (caller-owned).
|
||||
pub(crate) fn collect_block_inputs(
|
||||
&mut self,
|
||||
first_txinindex: usize,
|
||||
@@ -85,11 +95,11 @@ impl<'a> TxInReaders<'a> {
|
||||
) -> (Vec<Sats>, Vec<Height>, Vec<OutputType>, Vec<TypeIndex>) {
|
||||
let end = first_txinindex + input_count;
|
||||
let values: Vec<Sats> = self.txins.spent.value.collect_range_at(first_txinindex, end);
|
||||
let outpoints: Vec<OutPoint> = self.indexer.vecs.inputs.outpoint.collect_range_at(first_txinindex, end);
|
||||
self.indexer.vecs.inputs.outpoint.collect_range_into_at(first_txinindex, end, &mut self.outpoints_buf);
|
||||
let outputtypes: Vec<OutputType> = self.indexer.vecs.inputs.outputtype.collect_range_at(first_txinindex, end);
|
||||
let typeindexes: Vec<TypeIndex> = self.indexer.vecs.inputs.typeindex.collect_range_at(first_txinindex, end);
|
||||
|
||||
let prev_heights: Vec<Height> = outpoints
|
||||
let prev_heights: Vec<Height> = self.outpoints_buf
|
||||
.iter()
|
||||
.map(|outpoint| {
|
||||
if outpoint.is_coinbase() {
|
||||
|
||||
@@ -11,8 +11,6 @@ use brk_types::{
|
||||
use rustc_hash::FxHashMap;
|
||||
use vecdb::Bytes;
|
||||
|
||||
use crate::utils::OptionExt;
|
||||
|
||||
use super::{CachedUnrealizedState, Percentiles, UnrealizedState};
|
||||
|
||||
/// Type alias for the price-to-sats map used in cost basis data.
|
||||
@@ -97,17 +95,17 @@ impl CostBasisData {
|
||||
|
||||
pub(crate) fn iter(&self) -> impl Iterator<Item = (CentsCompact, &Sats)> {
|
||||
self.assert_pending_empty();
|
||||
self.state.u().base.map.iter().map(|(&k, v)| (k, v))
|
||||
self.state.as_ref().unwrap().base.map.iter().map(|(&k, v)| (k, v))
|
||||
}
|
||||
|
||||
pub(crate) fn is_empty(&self) -> bool {
|
||||
self.pending.is_empty() && self.state.u().base.map.is_empty()
|
||||
self.pending.is_empty() && self.state.as_ref().unwrap().base.map.is_empty()
|
||||
}
|
||||
|
||||
pub(crate) fn first_key_value(&self) -> Option<(CentsCompact, &Sats)> {
|
||||
self.assert_pending_empty();
|
||||
self.state
|
||||
.u()
|
||||
.as_ref().unwrap()
|
||||
.base
|
||||
.map
|
||||
.first_key_value()
|
||||
@@ -117,7 +115,7 @@ impl CostBasisData {
|
||||
pub(crate) fn last_key_value(&self) -> Option<(CentsCompact, &Sats)> {
|
||||
self.assert_pending_empty();
|
||||
self.state
|
||||
.u()
|
||||
.as_ref().unwrap()
|
||||
.base
|
||||
.map
|
||||
.last_key_value()
|
||||
@@ -127,13 +125,13 @@ impl CostBasisData {
|
||||
/// Get the exact cap_raw value (not recomputed from map).
|
||||
pub(crate) fn cap_raw(&self) -> CentsSats {
|
||||
self.assert_pending_empty();
|
||||
self.state.u().cap_raw
|
||||
self.state.as_ref().unwrap().cap_raw
|
||||
}
|
||||
|
||||
/// Get the exact investor_cap_raw value (not recomputed from map).
|
||||
pub(crate) fn investor_cap_raw(&self) -> CentsSquaredSats {
|
||||
self.assert_pending_empty();
|
||||
self.state.u().investor_cap_raw
|
||||
self.state.as_ref().unwrap().investor_cap_raw
|
||||
}
|
||||
|
||||
/// Increment with pre-computed typed values.
|
||||
@@ -181,7 +179,7 @@ impl CostBasisData {
|
||||
self.percentiles_dirty = true;
|
||||
}
|
||||
for (cents, (inc, dec)) in self.pending.drain() {
|
||||
let entry = self.state.um().base.map.entry(cents).or_default();
|
||||
let entry = self.state.as_mut().unwrap().base.map.entry(cents).or_default();
|
||||
*entry += inc;
|
||||
if *entry < dec {
|
||||
panic!(
|
||||
@@ -198,12 +196,12 @@ impl CostBasisData {
|
||||
}
|
||||
*entry -= dec;
|
||||
if *entry == Sats::ZERO {
|
||||
self.state.um().base.map.remove(¢s);
|
||||
self.state.as_mut().unwrap().base.map.remove(¢s);
|
||||
}
|
||||
}
|
||||
|
||||
// Apply raw values
|
||||
let state = self.state.um();
|
||||
let state = self.state.as_mut().unwrap();
|
||||
state.cap_raw += self.pending_raw.cap_inc;
|
||||
|
||||
// Check for underflow before subtracting
|
||||
@@ -271,7 +269,7 @@ impl CostBasisData {
|
||||
);
|
||||
}
|
||||
|
||||
let map = &self.state.u().base.map;
|
||||
let map = &self.state.as_ref().unwrap().base.map;
|
||||
|
||||
let date_state =
|
||||
date_price.map(|p| CachedUnrealizedState::compute_full_standalone(p.into(), map));
|
||||
@@ -336,7 +334,7 @@ impl CostBasisData {
|
||||
}
|
||||
}
|
||||
|
||||
fs::write(self.path_state(height), self.state.u().serialize()?)?;
|
||||
fs::write(self.path_state(height), self.state.as_ref().unwrap().serialize()?)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user