mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-08-04 22:53:05 -07:00
global: part X + 3
This commit is contained in:
Generated
+12
-2
@@ -155,6 +155,15 @@ version = "1.0.104"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470"
|
||||
|
||||
[[package]]
|
||||
name = "arc-swap"
|
||||
version = "1.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c049c0be4daef0b145cb3555416b3b8ef5b7888a38aea1a3a155801fe7b0810b"
|
||||
dependencies = [
|
||||
"rustversion",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.7.8"
|
||||
@@ -2317,6 +2326,7 @@ checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad"
|
||||
name = "lsm-tree"
|
||||
version = "0.3.6"
|
||||
dependencies = [
|
||||
"arc-swap",
|
||||
"byteorder-lite",
|
||||
"byteview",
|
||||
"criterion",
|
||||
@@ -2967,9 +2977,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.16"
|
||||
version = "0.4.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fcfdb36bda0c880c5931cdc7a2bcdc8ba4556847b9d912bca70bc94708711ad"
|
||||
checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
|
||||
@@ -27,12 +27,9 @@ pub fn main() -> Result<()> {
|
||||
);
|
||||
println!("Time for BytesVec write_json: {:?}", start.elapsed());
|
||||
|
||||
// Test empty_addr_index (LazyVecFrom1 wrapper) - computed access
|
||||
// Test empty_addr_index (LazyVec wrapper) - computed access
|
||||
let empty_index = &computer.distribution.addrs.empty_index;
|
||||
println!(
|
||||
"\nempty_addr_index (LazyVecFrom1) len: {}",
|
||||
empty_index.len()
|
||||
);
|
||||
println!("\nempty_addr_index (LazyVec) len: {}", empty_index.len());
|
||||
|
||||
let start = Instant::now();
|
||||
let mut buf = Vec::new();
|
||||
@@ -45,7 +42,7 @@ pub fn main() -> Result<()> {
|
||||
"empty_addr_index last item JSON: {}",
|
||||
String::from_utf8_lossy(&buf)
|
||||
);
|
||||
println!("Time for LazyVecFrom1 write_json: {:?}", start.elapsed());
|
||||
println!("Time for LazyVec write_json: {:?}", start.elapsed());
|
||||
|
||||
// Compare with funded versions
|
||||
let funded_data = &computer.distribution.addrs_data.funded;
|
||||
|
||||
@@ -5,7 +5,7 @@ use brk_types::{
|
||||
Bitcoin, Cents, CentsSigned, Dollars, Height, PartsPerMillionSigned64, StoredF64, Version,
|
||||
};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use vecdb::{AnyStoredVec, Exit, LazyVecFrom1, ReadableCloneableVec, ReadableVec, Rw, StorageMode};
|
||||
use vecdb::{AnyStoredVec, Exit, LazyVec, ReadableCloneableVec, ReadableVec, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
distribution::state::{CohortState, CostBasisOps, RealizedOps},
|
||||
@@ -23,7 +23,7 @@ use super::RealizedMinimal;
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct NegRealizedLoss {
|
||||
#[traversable(flatten)]
|
||||
pub base: LazyVecFrom1<Height, Dollars, Height, Cents>,
|
||||
pub base: LazyVec<Height, Dollars, Height, Cents>,
|
||||
pub sum: Windows<LazyPerBlock<Dollars, Cents>>,
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ impl RealizedCore {
|
||||
|
||||
let minimal = RealizedMinimal::forced_import(cfg)?;
|
||||
|
||||
let neg_loss_base = LazyVecFrom1::transformed::<NegCentsUnsignedToDollars>(
|
||||
let neg_loss_base = LazyVec::transformed::<NegCentsUnsignedToDollars>(
|
||||
&cfg.name("realized_loss_neg"),
|
||||
cfg.version + Version::ONE,
|
||||
minimal.loss.block.cents.read_only_boxed_clone(),
|
||||
|
||||
@@ -11,7 +11,7 @@ use brk_types::{
|
||||
use rayon::prelude::*;
|
||||
use tracing::{debug, info};
|
||||
use vecdb::{
|
||||
AnyStoredVec, AnyVec, BytesVec, Database, Exit, ImportOptions, ImportableVec, LazyVecFrom1,
|
||||
AnyStoredVec, AnyVec, BytesVec, Database, Exit, ImportOptions, ImportableVec, LazyVec,
|
||||
ReadableCloneableVec, ReadableVec, Rw, Stamp, StorageMode, WritableVec,
|
||||
};
|
||||
|
||||
@@ -55,10 +55,9 @@ pub struct AddrMetricsVecs<M: StorageMode = Rw> {
|
||||
pub delta: DeltaVecs,
|
||||
pub avg_amount: AvgAmountVecs<M>,
|
||||
#[traversable(wrap = "indexes", rename = "funded")]
|
||||
pub funded_index:
|
||||
LazyVecFrom1<FundedAddrIndex, FundedAddrIndex, FundedAddrIndex, FundedAddrData>,
|
||||
pub funded_index: LazyVec<FundedAddrIndex, FundedAddrIndex, FundedAddrIndex, FundedAddrData>,
|
||||
#[traversable(wrap = "indexes", rename = "empty")]
|
||||
pub empty_index: LazyVecFrom1<EmptyAddrIndex, EmptyAddrIndex, EmptyAddrIndex, EmptyAddrData>,
|
||||
pub empty_index: LazyVec<EmptyAddrIndex, EmptyAddrIndex, EmptyAddrIndex, EmptyAddrData>,
|
||||
}
|
||||
|
||||
impl AddrMetricsVecs {
|
||||
@@ -230,13 +229,13 @@ impl Vecs {
|
||||
)?;
|
||||
|
||||
// Identity mappings for traversable
|
||||
let funded_addr_index = LazyVecFrom1::init(
|
||||
let funded_addr_index = LazyVec::init(
|
||||
"funded_addr_index",
|
||||
funded_addr_data_version,
|
||||
funded_addr_index_to_funded_addr_data.read_only_boxed_clone(),
|
||||
|index, _| index,
|
||||
);
|
||||
let empty_addr_index = LazyVecFrom1::init(
|
||||
let empty_addr_index = LazyVec::init(
|
||||
"empty_addr_index",
|
||||
version,
|
||||
empty_addr_index_to_empty_addr_data.read_only_boxed_clone(),
|
||||
|
||||
@@ -6,7 +6,7 @@ use brk_types::{
|
||||
P2SHAddrIndex, P2SHBytes, P2TRAddrIndex, P2TRBytes, P2WPKHAddrIndex, P2WPKHBytes,
|
||||
P2WSHAddrIndex, P2WSHBytes, TxIndex, UnknownOutputIndex, Version,
|
||||
};
|
||||
use vecdb::{LazyVecFrom1, ReadableCloneableVec};
|
||||
use vecdb::{LazyVec, ReadableCloneableVec};
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct Vecs {
|
||||
@@ -26,83 +26,83 @@ pub struct Vecs {
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct P2PK33Vecs {
|
||||
pub identity: LazyVecFrom1<P2PK33AddrIndex, P2PK33AddrIndex, P2PK33AddrIndex, P2PK33Bytes>,
|
||||
pub addr: LazyVecFrom1<P2PK33AddrIndex, Addr, P2PK33AddrIndex, P2PK33Bytes>,
|
||||
pub identity: LazyVec<P2PK33AddrIndex, P2PK33AddrIndex, P2PK33AddrIndex, P2PK33Bytes>,
|
||||
pub addr: LazyVec<P2PK33AddrIndex, Addr, P2PK33AddrIndex, P2PK33Bytes>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct P2PK65Vecs {
|
||||
pub identity: LazyVecFrom1<P2PK65AddrIndex, P2PK65AddrIndex, P2PK65AddrIndex, P2PK65Bytes>,
|
||||
pub addr: LazyVecFrom1<P2PK65AddrIndex, Addr, P2PK65AddrIndex, P2PK65Bytes>,
|
||||
pub identity: LazyVec<P2PK65AddrIndex, P2PK65AddrIndex, P2PK65AddrIndex, P2PK65Bytes>,
|
||||
pub addr: LazyVec<P2PK65AddrIndex, Addr, P2PK65AddrIndex, P2PK65Bytes>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct P2PKHVecs {
|
||||
pub identity: LazyVecFrom1<P2PKHAddrIndex, P2PKHAddrIndex, P2PKHAddrIndex, P2PKHBytes>,
|
||||
pub addr: LazyVecFrom1<P2PKHAddrIndex, Addr, P2PKHAddrIndex, P2PKHBytes>,
|
||||
pub identity: LazyVec<P2PKHAddrIndex, P2PKHAddrIndex, P2PKHAddrIndex, P2PKHBytes>,
|
||||
pub addr: LazyVec<P2PKHAddrIndex, Addr, P2PKHAddrIndex, P2PKHBytes>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct P2SHVecs {
|
||||
pub identity: LazyVecFrom1<P2SHAddrIndex, P2SHAddrIndex, P2SHAddrIndex, P2SHBytes>,
|
||||
pub addr: LazyVecFrom1<P2SHAddrIndex, Addr, P2SHAddrIndex, P2SHBytes>,
|
||||
pub identity: LazyVec<P2SHAddrIndex, P2SHAddrIndex, P2SHAddrIndex, P2SHBytes>,
|
||||
pub addr: LazyVec<P2SHAddrIndex, Addr, P2SHAddrIndex, P2SHBytes>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct P2TRVecs {
|
||||
pub identity: LazyVecFrom1<P2TRAddrIndex, P2TRAddrIndex, P2TRAddrIndex, P2TRBytes>,
|
||||
pub addr: LazyVecFrom1<P2TRAddrIndex, Addr, P2TRAddrIndex, P2TRBytes>,
|
||||
pub identity: LazyVec<P2TRAddrIndex, P2TRAddrIndex, P2TRAddrIndex, P2TRBytes>,
|
||||
pub addr: LazyVec<P2TRAddrIndex, Addr, P2TRAddrIndex, P2TRBytes>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct P2WPKHVecs {
|
||||
pub identity: LazyVecFrom1<P2WPKHAddrIndex, P2WPKHAddrIndex, P2WPKHAddrIndex, P2WPKHBytes>,
|
||||
pub addr: LazyVecFrom1<P2WPKHAddrIndex, Addr, P2WPKHAddrIndex, P2WPKHBytes>,
|
||||
pub identity: LazyVec<P2WPKHAddrIndex, P2WPKHAddrIndex, P2WPKHAddrIndex, P2WPKHBytes>,
|
||||
pub addr: LazyVec<P2WPKHAddrIndex, Addr, P2WPKHAddrIndex, P2WPKHBytes>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct P2WSHVecs {
|
||||
pub identity: LazyVecFrom1<P2WSHAddrIndex, P2WSHAddrIndex, P2WSHAddrIndex, P2WSHBytes>,
|
||||
pub addr: LazyVecFrom1<P2WSHAddrIndex, Addr, P2WSHAddrIndex, P2WSHBytes>,
|
||||
pub identity: LazyVec<P2WSHAddrIndex, P2WSHAddrIndex, P2WSHAddrIndex, P2WSHBytes>,
|
||||
pub addr: LazyVec<P2WSHAddrIndex, Addr, P2WSHAddrIndex, P2WSHBytes>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct P2AVecs {
|
||||
pub identity: LazyVecFrom1<P2AAddrIndex, P2AAddrIndex, P2AAddrIndex, P2ABytes>,
|
||||
pub addr: LazyVecFrom1<P2AAddrIndex, Addr, P2AAddrIndex, P2ABytes>,
|
||||
pub identity: LazyVec<P2AAddrIndex, P2AAddrIndex, P2AAddrIndex, P2ABytes>,
|
||||
pub addr: LazyVec<P2AAddrIndex, Addr, P2AAddrIndex, P2ABytes>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct P2MSVecs {
|
||||
pub identity: LazyVecFrom1<P2MSOutputIndex, P2MSOutputIndex, P2MSOutputIndex, TxIndex>,
|
||||
pub identity: LazyVec<P2MSOutputIndex, P2MSOutputIndex, P2MSOutputIndex, TxIndex>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct EmptyVecs {
|
||||
pub identity: LazyVecFrom1<EmptyOutputIndex, EmptyOutputIndex, EmptyOutputIndex, TxIndex>,
|
||||
pub identity: LazyVec<EmptyOutputIndex, EmptyOutputIndex, EmptyOutputIndex, TxIndex>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct UnknownVecs {
|
||||
pub identity: LazyVecFrom1<UnknownOutputIndex, UnknownOutputIndex, UnknownOutputIndex, TxIndex>,
|
||||
pub identity: LazyVec<UnknownOutputIndex, UnknownOutputIndex, UnknownOutputIndex, TxIndex>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct OpReturnVecs {
|
||||
pub identity: LazyVecFrom1<OpReturnIndex, OpReturnIndex, OpReturnIndex, TxIndex>,
|
||||
pub identity: LazyVec<OpReturnIndex, OpReturnIndex, OpReturnIndex, TxIndex>,
|
||||
}
|
||||
|
||||
impl Vecs {
|
||||
pub(crate) fn forced_import(version: Version, indexer: &Indexer) -> Self {
|
||||
Self {
|
||||
p2pk33: P2PK33Vecs {
|
||||
identity: LazyVecFrom1::init(
|
||||
identity: LazyVec::init(
|
||||
"p2pk33_addr_index",
|
||||
version,
|
||||
indexer.vecs.addrs.p2pk33.bytes.read_only_boxed_clone(),
|
||||
|index, _| index,
|
||||
),
|
||||
addr: LazyVecFrom1::init(
|
||||
addr: LazyVec::init(
|
||||
"p2pk33_addr",
|
||||
version,
|
||||
indexer.vecs.addrs.p2pk33.bytes.read_only_boxed_clone(),
|
||||
@@ -110,13 +110,13 @@ impl Vecs {
|
||||
),
|
||||
},
|
||||
p2pk65: P2PK65Vecs {
|
||||
identity: LazyVecFrom1::init(
|
||||
identity: LazyVec::init(
|
||||
"p2pk65_addr_index",
|
||||
version,
|
||||
indexer.vecs.addrs.p2pk65.bytes.read_only_boxed_clone(),
|
||||
|index, _| index,
|
||||
),
|
||||
addr: LazyVecFrom1::init(
|
||||
addr: LazyVec::init(
|
||||
"p2pk65_addr",
|
||||
version,
|
||||
indexer.vecs.addrs.p2pk65.bytes.read_only_boxed_clone(),
|
||||
@@ -124,13 +124,13 @@ impl Vecs {
|
||||
),
|
||||
},
|
||||
p2pkh: P2PKHVecs {
|
||||
identity: LazyVecFrom1::init(
|
||||
identity: LazyVec::init(
|
||||
"p2pkh_addr_index",
|
||||
version,
|
||||
indexer.vecs.addrs.p2pkh.bytes.read_only_boxed_clone(),
|
||||
|index, _| index,
|
||||
),
|
||||
addr: LazyVecFrom1::init(
|
||||
addr: LazyVec::init(
|
||||
"p2pkh_addr",
|
||||
version,
|
||||
indexer.vecs.addrs.p2pkh.bytes.read_only_boxed_clone(),
|
||||
@@ -138,13 +138,13 @@ impl Vecs {
|
||||
),
|
||||
},
|
||||
p2sh: P2SHVecs {
|
||||
identity: LazyVecFrom1::init(
|
||||
identity: LazyVec::init(
|
||||
"p2sh_addr_index",
|
||||
version,
|
||||
indexer.vecs.addrs.p2sh.bytes.read_only_boxed_clone(),
|
||||
|index, _| index,
|
||||
),
|
||||
addr: LazyVecFrom1::init(
|
||||
addr: LazyVec::init(
|
||||
"p2sh_addr",
|
||||
version,
|
||||
indexer.vecs.addrs.p2sh.bytes.read_only_boxed_clone(),
|
||||
@@ -152,13 +152,13 @@ impl Vecs {
|
||||
),
|
||||
},
|
||||
p2tr: P2TRVecs {
|
||||
identity: LazyVecFrom1::init(
|
||||
identity: LazyVec::init(
|
||||
"p2tr_addr_index",
|
||||
version,
|
||||
indexer.vecs.addrs.p2tr.bytes.read_only_boxed_clone(),
|
||||
|index, _| index,
|
||||
),
|
||||
addr: LazyVecFrom1::init(
|
||||
addr: LazyVec::init(
|
||||
"p2tr_addr",
|
||||
version,
|
||||
indexer.vecs.addrs.p2tr.bytes.read_only_boxed_clone(),
|
||||
@@ -166,13 +166,13 @@ impl Vecs {
|
||||
),
|
||||
},
|
||||
p2wpkh: P2WPKHVecs {
|
||||
identity: LazyVecFrom1::init(
|
||||
identity: LazyVec::init(
|
||||
"p2wpkh_addr_index",
|
||||
version,
|
||||
indexer.vecs.addrs.p2wpkh.bytes.read_only_boxed_clone(),
|
||||
|index, _| index,
|
||||
),
|
||||
addr: LazyVecFrom1::init(
|
||||
addr: LazyVec::init(
|
||||
"p2wpkh_addr",
|
||||
version,
|
||||
indexer.vecs.addrs.p2wpkh.bytes.read_only_boxed_clone(),
|
||||
@@ -180,13 +180,13 @@ impl Vecs {
|
||||
),
|
||||
},
|
||||
p2wsh: P2WSHVecs {
|
||||
identity: LazyVecFrom1::init(
|
||||
identity: LazyVec::init(
|
||||
"p2wsh_addr_index",
|
||||
version,
|
||||
indexer.vecs.addrs.p2wsh.bytes.read_only_boxed_clone(),
|
||||
|index, _| index,
|
||||
),
|
||||
addr: LazyVecFrom1::init(
|
||||
addr: LazyVec::init(
|
||||
"p2wsh_addr",
|
||||
version,
|
||||
indexer.vecs.addrs.p2wsh.bytes.read_only_boxed_clone(),
|
||||
@@ -194,13 +194,13 @@ impl Vecs {
|
||||
),
|
||||
},
|
||||
p2a: P2AVecs {
|
||||
identity: LazyVecFrom1::init(
|
||||
identity: LazyVec::init(
|
||||
"p2a_addr_index",
|
||||
version,
|
||||
indexer.vecs.addrs.p2a.bytes.read_only_boxed_clone(),
|
||||
|index, _| index,
|
||||
),
|
||||
addr: LazyVecFrom1::init(
|
||||
addr: LazyVec::init(
|
||||
"p2a_addr",
|
||||
version,
|
||||
indexer.vecs.addrs.p2a.bytes.read_only_boxed_clone(),
|
||||
@@ -208,7 +208,7 @@ impl Vecs {
|
||||
),
|
||||
},
|
||||
p2ms: P2MSVecs {
|
||||
identity: LazyVecFrom1::init(
|
||||
identity: LazyVec::init(
|
||||
"p2ms_output_index",
|
||||
version,
|
||||
indexer
|
||||
@@ -221,7 +221,7 @@ impl Vecs {
|
||||
),
|
||||
},
|
||||
empty: EmptyVecs {
|
||||
identity: LazyVecFrom1::init(
|
||||
identity: LazyVec::init(
|
||||
"empty_output_index",
|
||||
version,
|
||||
indexer
|
||||
@@ -234,7 +234,7 @@ impl Vecs {
|
||||
),
|
||||
},
|
||||
unknown: UnknownVecs {
|
||||
identity: LazyVecFrom1::init(
|
||||
identity: LazyVec::init(
|
||||
"unknown_output_index",
|
||||
version,
|
||||
indexer
|
||||
@@ -247,7 +247,7 @@ impl Vecs {
|
||||
),
|
||||
},
|
||||
op_return: OpReturnVecs {
|
||||
identity: LazyVecFrom1::init(
|
||||
identity: LazyVec::init(
|
||||
"op_return_index",
|
||||
version,
|
||||
indexer.vecs.op_return.to_tx_index.read_only_boxed_clone(),
|
||||
|
||||
@@ -3,29 +3,27 @@ use brk_types::{
|
||||
Day1, Day3, Epoch, Halving, Height, Hour1, Hour4, Hour12, Minute10, Minute30, Month1, Month3,
|
||||
Month6, StoredU64, Timestamp, Version, Week1, Year1, Year10,
|
||||
};
|
||||
use vecdb::{
|
||||
CachedBoxedVec, CachedReadableVec, CachedVec, LazyVecFrom1, ReadableBoxedVec, VecValue,
|
||||
};
|
||||
use vecdb::{CachedBoxedVec, CachedReadableVec, CachedVec, LazyVec, ReadableBoxedVec, VecValue};
|
||||
|
||||
use crate::internal::LazyPreviousDeltaVec;
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct Vecs {
|
||||
pub minute10: LazyVecFrom1<Height, Minute10, Height, Timestamp>,
|
||||
pub minute30: LazyVecFrom1<Height, Minute30, Height, Timestamp>,
|
||||
pub hour1: LazyVecFrom1<Height, Hour1, Height, Timestamp>,
|
||||
pub hour4: LazyVecFrom1<Height, Hour4, Height, Timestamp>,
|
||||
pub hour12: LazyVecFrom1<Height, Hour12, Height, Timestamp>,
|
||||
pub day1: CachedVec<LazyVecFrom1<Height, Day1, Height, Timestamp>>,
|
||||
pub day3: LazyVecFrom1<Height, Day3, Height, Timestamp>,
|
||||
pub epoch: LazyVecFrom1<Height, Epoch, Height, Timestamp>,
|
||||
pub halving: LazyVecFrom1<Height, Halving, Height, Timestamp>,
|
||||
pub week1: LazyVecFrom1<Height, Week1, Height, Timestamp>,
|
||||
pub month1: LazyVecFrom1<Height, Month1, Height, Timestamp>,
|
||||
pub month3: LazyVecFrom1<Height, Month3, Height, Timestamp>,
|
||||
pub month6: LazyVecFrom1<Height, Month6, Height, Timestamp>,
|
||||
pub year1: LazyVecFrom1<Height, Year1, Height, Timestamp>,
|
||||
pub year10: LazyVecFrom1<Height, Year10, Height, Timestamp>,
|
||||
pub minute10: LazyVec<Height, Minute10, Height, Timestamp>,
|
||||
pub minute30: LazyVec<Height, Minute30, Height, Timestamp>,
|
||||
pub hour1: LazyVec<Height, Hour1, Height, Timestamp>,
|
||||
pub hour4: LazyVec<Height, Hour4, Height, Timestamp>,
|
||||
pub hour12: LazyVec<Height, Hour12, Height, Timestamp>,
|
||||
pub day1: CachedVec<LazyVec<Height, Day1, Height, Timestamp>>,
|
||||
pub day3: LazyVec<Height, Day3, Height, Timestamp>,
|
||||
pub epoch: LazyVec<Height, Epoch, Height, Timestamp>,
|
||||
pub halving: LazyVec<Height, Halving, Height, Timestamp>,
|
||||
pub week1: LazyVec<Height, Week1, Height, Timestamp>,
|
||||
pub month1: LazyVec<Height, Month1, Height, Timestamp>,
|
||||
pub month3: LazyVec<Height, Month3, Height, Timestamp>,
|
||||
pub month6: LazyVec<Height, Month6, Height, Timestamp>,
|
||||
pub year1: LazyVec<Height, Year1, Height, Timestamp>,
|
||||
pub year10: LazyVec<Height, Year10, Height, Timestamp>,
|
||||
pub tx_index_count: LazyPreviousDeltaVec<Height, StoredU64>,
|
||||
}
|
||||
|
||||
@@ -91,8 +89,8 @@ impl Vecs {
|
||||
name: &str,
|
||||
timestamps: ReadableBoxedVec<Height, Timestamp>,
|
||||
compute: fn(Height, Timestamp) -> T,
|
||||
) -> LazyVecFrom1<Height, T, Height, Timestamp> {
|
||||
LazyVecFrom1::init(name, Version::ZERO, timestamps, compute)
|
||||
) -> LazyVec<Height, T, Height, Timestamp> {
|
||||
LazyVec::init(name, Version::ZERO, timestamps, compute)
|
||||
}
|
||||
|
||||
pub(crate) fn day1_from_timestamp(timestamp: Timestamp) -> Day1 {
|
||||
|
||||
@@ -8,8 +8,8 @@ use brk_types::{
|
||||
};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use vecdb::{
|
||||
AnyVec, CachedVec, Database, EagerVec, Exit, ImportableVec, LazyVecFrom1, PcoVec,
|
||||
ReadableBoxedVec, ReadableVec, Rw, StorageMode, Version,
|
||||
AnyVec, CachedVec, Database, EagerVec, Exit, ImportableVec, LazyVec, PcoVec, ReadableBoxedVec,
|
||||
ReadableVec, Rw, StorageMode, Version,
|
||||
};
|
||||
|
||||
use crate::internal::PerResolution;
|
||||
@@ -30,19 +30,19 @@ pub struct Timestamps<M: StorageMode = Rw> {
|
||||
#[traversable(flatten)]
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub resolutions: PerResolution<
|
||||
LazyVecFrom1<Minute10, Timestamp, Minute10, Height>,
|
||||
LazyVecFrom1<Minute30, Timestamp, Minute30, Height>,
|
||||
LazyVecFrom1<Hour1, Timestamp, Hour1, Height>,
|
||||
LazyVecFrom1<Hour4, Timestamp, Hour4, Height>,
|
||||
LazyVecFrom1<Hour12, Timestamp, Hour12, Height>,
|
||||
LazyVecFrom1<Day1, Timestamp, Day1, Height>,
|
||||
LazyVecFrom1<Day3, Timestamp, Day3, Height>,
|
||||
LazyVecFrom1<Week1, Timestamp, Week1, Height>,
|
||||
LazyVecFrom1<Month1, Timestamp, Month1, Height>,
|
||||
LazyVecFrom1<Month3, Timestamp, Month3, Height>,
|
||||
LazyVecFrom1<Month6, Timestamp, Month6, Height>,
|
||||
LazyVecFrom1<Year1, Timestamp, Year1, Height>,
|
||||
LazyVecFrom1<Year10, Timestamp, Year10, Height>,
|
||||
LazyVec<Minute10, Timestamp, Minute10, Height>,
|
||||
LazyVec<Minute30, Timestamp, Minute30, Height>,
|
||||
LazyVec<Hour1, Timestamp, Hour1, Height>,
|
||||
LazyVec<Hour4, Timestamp, Hour4, Height>,
|
||||
LazyVec<Hour12, Timestamp, Hour12, Height>,
|
||||
LazyVec<Day1, Timestamp, Day1, Height>,
|
||||
LazyVec<Day3, Timestamp, Day3, Height>,
|
||||
LazyVec<Week1, Timestamp, Week1, Height>,
|
||||
LazyVec<Month1, Timestamp, Month1, Height>,
|
||||
LazyVec<Month3, Timestamp, Month3, Height>,
|
||||
LazyVec<Month6, Timestamp, Month6, Height>,
|
||||
LazyVec<Year1, Timestamp, Year1, Height>,
|
||||
LazyVec<Year10, Timestamp, Year10, Height>,
|
||||
BoundaryTimestampVec<Halving>,
|
||||
BoundaryTimestampVec<Epoch>,
|
||||
>,
|
||||
@@ -81,7 +81,7 @@ impl Timestamps {
|
||||
) -> Self {
|
||||
macro_rules! period {
|
||||
($field:ident) => {
|
||||
LazyVecFrom1::init(
|
||||
LazyVec::init(
|
||||
"timestamp",
|
||||
version,
|
||||
$field.first_height.read_only_boxed_clone(),
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
use brk_indexer::Indexer;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{TxIndex, Txid, Version};
|
||||
use vecdb::{LazyVecFrom1, ReadableCloneableVec};
|
||||
use vecdb::{LazyVec, ReadableCloneableVec};
|
||||
|
||||
use crate::internal::LazyIndexCountVec;
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct Vecs {
|
||||
pub identity: LazyVecFrom1<TxIndex, TxIndex, TxIndex, Txid>,
|
||||
pub identity: LazyVec<TxIndex, TxIndex, TxIndex, Txid>,
|
||||
pub input_count: LazyIndexCountVec<TxIndex, brk_types::TxInIndex>,
|
||||
pub output_count: LazyIndexCountVec<TxIndex, brk_types::TxOutIndex>,
|
||||
}
|
||||
@@ -15,7 +15,7 @@ pub struct Vecs {
|
||||
impl Vecs {
|
||||
pub(crate) fn new(version: Version, indexer: &Indexer) -> Self {
|
||||
Self {
|
||||
identity: LazyVecFrom1::init(
|
||||
identity: LazyVec::init(
|
||||
"tx_index",
|
||||
version,
|
||||
indexer.vecs.transactions.txid.read_only_boxed_clone(),
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
use brk_indexer::Indexer;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{OutPoint, TxInIndex, Version};
|
||||
use vecdb::{LazyVecFrom1, ReadableCloneableVec};
|
||||
use vecdb::{LazyVec, ReadableCloneableVec};
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct Vecs {
|
||||
pub identity: LazyVecFrom1<TxInIndex, TxInIndex, TxInIndex, OutPoint>,
|
||||
pub identity: LazyVec<TxInIndex, TxInIndex, TxInIndex, OutPoint>,
|
||||
}
|
||||
|
||||
impl Vecs {
|
||||
pub(crate) fn forced_import(version: Version, indexer: &Indexer) -> Self {
|
||||
Self {
|
||||
identity: LazyVecFrom1::init(
|
||||
identity: LazyVec::init(
|
||||
"txin_index",
|
||||
version,
|
||||
indexer.vecs.inputs.outpoint.read_only_boxed_clone(),
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
use brk_indexer::Indexer;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Sats, TxOutIndex, Version};
|
||||
use vecdb::{LazyVecFrom1, ReadableCloneableVec};
|
||||
use vecdb::{LazyVec, ReadableCloneableVec};
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct Vecs {
|
||||
pub identity: LazyVecFrom1<TxOutIndex, TxOutIndex, TxOutIndex, Sats>,
|
||||
pub identity: LazyVec<TxOutIndex, TxOutIndex, TxOutIndex, Sats>,
|
||||
}
|
||||
|
||||
impl Vecs {
|
||||
pub(crate) fn forced_import(version: Version, indexer: &Indexer) -> Self {
|
||||
Self {
|
||||
identity: LazyVecFrom1::init(
|
||||
identity: LazyVec::init(
|
||||
"txout_index",
|
||||
version,
|
||||
indexer.vecs.outputs.value.read_only_boxed_clone(),
|
||||
|
||||
@@ -53,10 +53,9 @@ impl Vecs {
|
||||
};
|
||||
|
||||
itype_cursor.advance(fi_in - itype_cursor.position());
|
||||
for _ in fi_in..next_fi_in {
|
||||
let otype = itype_cursor.next().unwrap();
|
||||
itype_cursor.for_each(next_fi_in - fi_in, |otype| {
|
||||
per_tx[otype as usize] += 1;
|
||||
}
|
||||
});
|
||||
Ok(())
|
||||
},
|
||||
|agg| {
|
||||
|
||||
@@ -5,7 +5,7 @@ use brk_types::{
|
||||
};
|
||||
use schemars::JsonSchema;
|
||||
use serde::Serialize;
|
||||
use vecdb::{Formattable, LazyVecFrom1, ReadableCloneableVec, UnaryTransform, VecValue};
|
||||
use vecdb::{Formattable, LazyVec, ReadableCloneableVec, UnaryTransform, VecValue};
|
||||
|
||||
use crate::indexes;
|
||||
|
||||
@@ -15,22 +15,22 @@ pub struct ConstantVecs<T>
|
||||
where
|
||||
T: VecValue + Formattable + Serialize + JsonSchema,
|
||||
{
|
||||
pub height: LazyVecFrom1<Height, T, Height, Minute10>,
|
||||
pub minute10: LazyVecFrom1<Minute10, T, Minute10, Height>,
|
||||
pub minute30: LazyVecFrom1<Minute30, T, Minute30, Height>,
|
||||
pub hour1: LazyVecFrom1<Hour1, T, Hour1, Height>,
|
||||
pub hour4: LazyVecFrom1<Hour4, T, Hour4, Height>,
|
||||
pub hour12: LazyVecFrom1<Hour12, T, Hour12, Height>,
|
||||
pub day1: LazyVecFrom1<Day1, T, Day1, Height>,
|
||||
pub day3: LazyVecFrom1<Day3, T, Day3, Height>,
|
||||
pub week1: LazyVecFrom1<Week1, T, Week1, Height>,
|
||||
pub month1: LazyVecFrom1<Month1, T, Month1, Height>,
|
||||
pub month3: LazyVecFrom1<Month3, T, Month3, Height>,
|
||||
pub month6: LazyVecFrom1<Month6, T, Month6, Height>,
|
||||
pub year1: LazyVecFrom1<Year1, T, Year1, Height>,
|
||||
pub year10: LazyVecFrom1<Year10, T, Year10, Height>,
|
||||
pub halving: LazyVecFrom1<Halving, T, Halving, Height>,
|
||||
pub epoch: LazyVecFrom1<Epoch, T, Epoch, Height>,
|
||||
pub height: LazyVec<Height, T, Height, Minute10>,
|
||||
pub minute10: LazyVec<Minute10, T, Minute10, Height>,
|
||||
pub minute30: LazyVec<Minute30, T, Minute30, Height>,
|
||||
pub hour1: LazyVec<Hour1, T, Hour1, Height>,
|
||||
pub hour4: LazyVec<Hour4, T, Hour4, Height>,
|
||||
pub hour12: LazyVec<Hour12, T, Hour12, Height>,
|
||||
pub day1: LazyVec<Day1, T, Day1, Height>,
|
||||
pub day3: LazyVec<Day3, T, Day3, Height>,
|
||||
pub week1: LazyVec<Week1, T, Week1, Height>,
|
||||
pub month1: LazyVec<Month1, T, Month1, Height>,
|
||||
pub month3: LazyVec<Month3, T, Month3, Height>,
|
||||
pub month6: LazyVec<Month6, T, Month6, Height>,
|
||||
pub year1: LazyVec<Year1, T, Year1, Height>,
|
||||
pub year10: LazyVec<Year10, T, Year10, Height>,
|
||||
pub halving: LazyVec<Halving, T, Halving, Height>,
|
||||
pub epoch: LazyVec<Epoch, T, Epoch, Height>,
|
||||
}
|
||||
|
||||
impl<T: VecValue + Formattable + Serialize + JsonSchema> ConstantVecs<T> {
|
||||
@@ -55,7 +55,7 @@ impl<T: VecValue + Formattable + Serialize + JsonSchema> ConstantVecs<T> {
|
||||
{
|
||||
macro_rules! period {
|
||||
($idx:ident) => {
|
||||
LazyVecFrom1::init(
|
||||
LazyVec::init(
|
||||
name,
|
||||
version,
|
||||
indexes.$idx.first_height.read_only_boxed_clone(),
|
||||
@@ -65,7 +65,7 @@ impl<T: VecValue + Formattable + Serialize + JsonSchema> ConstantVecs<T> {
|
||||
}
|
||||
|
||||
Self {
|
||||
height: LazyVecFrom1::init(
|
||||
height: LazyVec::init(
|
||||
name,
|
||||
version,
|
||||
indexes.height.minute10.read_only_boxed_clone(),
|
||||
|
||||
@@ -9,15 +9,15 @@ use brk_types::{
|
||||
use schemars::JsonSchema;
|
||||
use serde::Serialize;
|
||||
use vecdb::{
|
||||
AnyExportableVec, AnyVec, Database, EagerVec, Formattable, ImportableVec, LazyVecFrom1,
|
||||
PcoVec, PcoVecValue, ReadableBoxedVec, ReadableCloneableVec, ReadableVec, Rw, StorageMode,
|
||||
TypedVec, UnaryTransform, VecIndex, VecValue, short_type_name,
|
||||
AnyExportableVec, AnyVec, Database, EagerVec, Formattable, ImportableVec, LazyVec, PcoVec,
|
||||
PcoVecValue, ReadableBoxedVec, ReadableCloneableVec, ReadableVec, Rw, StorageMode, TypedVec,
|
||||
UnaryTransform, VecIndex, VecValue, short_type_name,
|
||||
};
|
||||
|
||||
use crate::indexes;
|
||||
|
||||
type StoredDay<T, M> = <M as StorageMode>::Stored<EagerVec<PcoVec<Day1, T>>>;
|
||||
type DayMapping<I, T> = LazyVecFrom1<I, Day1, I, T>;
|
||||
type DayMapping<I, T> = LazyVec<I, Day1, I, T>;
|
||||
type Repeated<I, T> = DailyView<I, T, RepeatDay>;
|
||||
type Last<I, T> = DailyView<I, T, LastDay>;
|
||||
|
||||
@@ -49,7 +49,7 @@ pub(crate) struct DailyMappings {
|
||||
|
||||
impl DailyMappings {
|
||||
pub(crate) fn new(indexes: &indexes::Vecs) -> Self {
|
||||
let height = LazyVecFrom1::init(
|
||||
let height = LazyVec::init(
|
||||
"day1",
|
||||
Version::ZERO,
|
||||
indexes.height.day1_read_only_boxed_clone(),
|
||||
@@ -79,13 +79,13 @@ impl DailyMappings {
|
||||
fn timestamp_mapping<I: VecIndex>(
|
||||
source: ReadableBoxedVec<I, Timestamp>,
|
||||
) -> DayMapping<I, Timestamp> {
|
||||
LazyVecFrom1::init("day1", Version::ZERO, source, |_, timestamp| {
|
||||
LazyVec::init("day1", Version::ZERO, source, |_, timestamp| {
|
||||
Day1::try_from(Date::from(timestamp)).unwrap_or_default()
|
||||
})
|
||||
}
|
||||
|
||||
fn date_mapping<I: VecIndex>(source: ReadableBoxedVec<I, Date>) -> DayMapping<I, Date> {
|
||||
LazyVecFrom1::init("day1", Version::ZERO, source, |_, date| {
|
||||
LazyVec::init("day1", Version::ZERO, source, |_, date| {
|
||||
Day1::try_from(date).unwrap_or_default()
|
||||
})
|
||||
}
|
||||
@@ -172,7 +172,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
type LazyDay<T, S> = LazyVecFrom1<Day1, T, Day1, S>;
|
||||
type LazyDay<T, S> = LazyVec<Day1, T, Day1, S>;
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
#[traversable(merge)]
|
||||
@@ -200,7 +200,7 @@ where
|
||||
where
|
||||
F: UnaryTransform<S, T>,
|
||||
{
|
||||
let day1 = LazyVecFrom1::transformed::<F>(name, version, source);
|
||||
let day1 = LazyVec::transformed::<F>(name, version, source);
|
||||
let views = Box::new(DailyViews::new(
|
||||
name,
|
||||
day1.read_only_boxed_clone(),
|
||||
@@ -540,7 +540,7 @@ fn last_source_index(mapping: &[Day1], index: usize, source_len: usize) -> Optio
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use brk_types::StoredF64;
|
||||
use brk_types::{StoredBool, StoredF64};
|
||||
use vecdb::{AnyStoredVec, WritableVec};
|
||||
|
||||
#[test]
|
||||
@@ -611,4 +611,50 @@ mod tests {
|
||||
drop(db);
|
||||
std::fs::remove_dir_all(path).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn repeated_view_supports_stored_booleans() {
|
||||
let suffix = std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let path =
|
||||
std::env::temp_dir().join(format!("brk-daily-bool-{}-{suffix}", std::process::id()));
|
||||
let db = Database::open(&path).unwrap();
|
||||
|
||||
let mut source: EagerVec<PcoVec<Day1, StoredBool>> =
|
||||
EagerVec::forced_import(&db, "source", Version::ONE).unwrap();
|
||||
let mut mapping: EagerVec<PcoVec<Height, Day1>> =
|
||||
EagerVec::forced_import(&db, "mapping", Version::ONE).unwrap();
|
||||
source.push(StoredBool::FALSE);
|
||||
source.push(StoredBool::TRUE);
|
||||
for day in [0, 0, 1, 1] {
|
||||
mapping.push(Day1::from(day));
|
||||
}
|
||||
source.write().unwrap();
|
||||
mapping.write().unwrap();
|
||||
|
||||
let view = DailyView::<Height, StoredBool, RepeatDay>::new(
|
||||
"test",
|
||||
Version::ONE,
|
||||
source.read_only_boxed_clone(),
|
||||
mapping.read_only_boxed_clone(),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
view.collect_range_at(0, 4),
|
||||
vec![
|
||||
Some(StoredBool::FALSE),
|
||||
Some(StoredBool::FALSE),
|
||||
Some(StoredBool::TRUE),
|
||||
Some(StoredBool::TRUE),
|
||||
]
|
||||
);
|
||||
|
||||
drop(view);
|
||||
drop(mapping);
|
||||
drop(source);
|
||||
drop(db);
|
||||
std::fs::remove_dir_all(path).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ use brk_types::{
|
||||
};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{LazyVecFrom1, ReadableCloneableVec, UnaryTransform, VecValue};
|
||||
use vecdb::{LazyVec, ReadableCloneableVec, UnaryTransform, VecValue};
|
||||
|
||||
use crate::internal::{ComputedVecValue, PerResolution};
|
||||
|
||||
@@ -14,21 +14,21 @@ use crate::internal::{ComputedVecValue, PerResolution};
|
||||
pub struct LazyIndexes<T, S>(
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub PerResolution<
|
||||
LazyVecFrom1<Minute10, T, Minute10, S>,
|
||||
LazyVecFrom1<Minute30, T, Minute30, S>,
|
||||
LazyVecFrom1<Hour1, T, Hour1, S>,
|
||||
LazyVecFrom1<Hour4, T, Hour4, S>,
|
||||
LazyVecFrom1<Hour12, T, Hour12, S>,
|
||||
LazyVecFrom1<Day1, T, Day1, S>,
|
||||
LazyVecFrom1<Day3, T, Day3, S>,
|
||||
LazyVecFrom1<Week1, T, Week1, S>,
|
||||
LazyVecFrom1<Month1, T, Month1, S>,
|
||||
LazyVecFrom1<Month3, T, Month3, S>,
|
||||
LazyVecFrom1<Month6, T, Month6, S>,
|
||||
LazyVecFrom1<Year1, T, Year1, S>,
|
||||
LazyVecFrom1<Year10, T, Year10, S>,
|
||||
LazyVecFrom1<Halving, T, Halving, S>,
|
||||
LazyVecFrom1<Epoch, T, Epoch, S>,
|
||||
LazyVec<Minute10, T, Minute10, S>,
|
||||
LazyVec<Minute30, T, Minute30, S>,
|
||||
LazyVec<Hour1, T, Hour1, S>,
|
||||
LazyVec<Hour4, T, Hour4, S>,
|
||||
LazyVec<Hour12, T, Hour12, S>,
|
||||
LazyVec<Day1, T, Day1, S>,
|
||||
LazyVec<Day3, T, Day3, S>,
|
||||
LazyVec<Week1, T, Week1, S>,
|
||||
LazyVec<Month1, T, Month1, S>,
|
||||
LazyVec<Month3, T, Month3, S>,
|
||||
LazyVec<Month6, T, Month6, S>,
|
||||
LazyVec<Year1, T, Year1, S>,
|
||||
LazyVec<Year10, T, Year10, S>,
|
||||
LazyVec<Halving, T, Halving, S>,
|
||||
LazyVec<Epoch, T, Epoch, S>,
|
||||
>,
|
||||
)
|
||||
where
|
||||
@@ -52,7 +52,7 @@ where
|
||||
{
|
||||
macro_rules! period {
|
||||
($idx:ident) => {
|
||||
LazyVecFrom1::transformed::<Transform>(
|
||||
LazyVec::transformed::<Transform>(
|
||||
name,
|
||||
version,
|
||||
source.$idx.read_only_boxed_clone(),
|
||||
|
||||
+3
-3
@@ -2,7 +2,7 @@ use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, StoredU16, StoredU64, Version};
|
||||
use vecdb::{
|
||||
AnyStoredVec, AnyVec, CachedVec, Database, EagerVec, ImportableVec, LazyVecFrom1, PcoVec, Rw,
|
||||
AnyStoredVec, AnyVec, CachedVec, Database, EagerVec, ImportableVec, LazyVec, PcoVec, Rw,
|
||||
StorageMode, WritableVec,
|
||||
};
|
||||
|
||||
@@ -16,7 +16,7 @@ use crate::{
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct CachedCountPerBlockCumulativeRolling<M: StorageMode = Rw> {
|
||||
pub block: LazyVecFrom1<Height, StoredU64, Height, StoredU16>,
|
||||
pub block: LazyVec<Height, StoredU64, Height, StoredU16>,
|
||||
pub cumulative: LazyPerBlock<StoredU64>,
|
||||
pub sum: LazyRollingSumsFromHeight<StoredU64>,
|
||||
pub average: LazyRollingAvgsFromHeight<StoredU64>,
|
||||
@@ -36,7 +36,7 @@ impl CachedCountPerBlockCumulativeRolling {
|
||||
) -> Result<Self> {
|
||||
let source = CachedVec::wrap(EagerVec::forced_import(db, name, version)?);
|
||||
let cached_cumulative = CachedBlockCountReader::new(source.read_only_cached_boxed_clone());
|
||||
let block = LazyVecFrom1::transformed::<StoredU16ToStoredU64>(
|
||||
let block = LazyVec::transformed::<StoredU16ToStoredU64>(
|
||||
name,
|
||||
version,
|
||||
source.read_only_boxed_clone(),
|
||||
|
||||
@@ -5,7 +5,7 @@ use brk_traversable::Traversable;
|
||||
use brk_types::{Height, Version};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{
|
||||
Database, Exit, LazyVecFrom1, ReadableCloneableVec, ReadableVec, Rw, StorageMode, VecValue,
|
||||
Database, Exit, LazyVec, ReadableCloneableVec, ReadableVec, Rw, StorageMode, VecValue,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
@@ -21,7 +21,7 @@ where
|
||||
T: NumericValue + JsonSchema,
|
||||
S: VecValue,
|
||||
{
|
||||
pub block: LazyVecFrom1<Height, T, Height, S>,
|
||||
pub block: LazyVec<Height, T, Height, S>,
|
||||
pub cumulative: PerBlock<T, M>,
|
||||
#[traversable(flatten)]
|
||||
pub rolling: RollingComplete<T, M>,
|
||||
@@ -41,8 +41,7 @@ where
|
||||
indexes: &indexes::Vecs,
|
||||
cached_starts: &Windows<&CachedWindowStartVec>,
|
||||
) -> Result<Self> {
|
||||
let block =
|
||||
LazyVecFrom1::init(name, version, source.read_only_boxed_clone(), compute_block);
|
||||
let block = LazyVec::init(name, version, source.read_only_boxed_clone(), compute_block);
|
||||
let cumulative =
|
||||
PerBlock::forced_import(db, &format!("{name}_cumulative"), version, indexes)?;
|
||||
let rolling = RollingComplete::forced_import(
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, Version};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{LazyVecFrom1, ReadableCloneableVec, UnaryTransform, VecIndex};
|
||||
use vecdb::{LazyVec, ReadableCloneableVec, UnaryTransform, VecIndex};
|
||||
|
||||
use crate::internal::{ComputedVecValue, DistributionStats, PerBlockDistribution};
|
||||
|
||||
/// Lazy analog of `Distribution<T>`: 7 `LazyVecFrom1` fields,
|
||||
/// Lazy analog of `Distribution<T>`: 7 `LazyVec` fields,
|
||||
/// each derived by transforming the corresponding field of a source `PerBlockDistribution<S1T>`.
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct LazyDistribution<I, T, S1T>
|
||||
@@ -14,13 +14,13 @@ where
|
||||
T: ComputedVecValue + JsonSchema,
|
||||
S1T: ComputedVecValue,
|
||||
{
|
||||
pub min: LazyVecFrom1<I, T, I, S1T>,
|
||||
pub max: LazyVecFrom1<I, T, I, S1T>,
|
||||
pub pct10: LazyVecFrom1<I, T, I, S1T>,
|
||||
pub pct25: LazyVecFrom1<I, T, I, S1T>,
|
||||
pub median: LazyVecFrom1<I, T, I, S1T>,
|
||||
pub pct75: LazyVecFrom1<I, T, I, S1T>,
|
||||
pub pct90: LazyVecFrom1<I, T, I, S1T>,
|
||||
pub min: LazyVec<I, T, I, S1T>,
|
||||
pub max: LazyVec<I, T, I, S1T>,
|
||||
pub pct10: LazyVec<I, T, I, S1T>,
|
||||
pub pct25: LazyVec<I, T, I, S1T>,
|
||||
pub median: LazyVec<I, T, I, S1T>,
|
||||
pub pct75: LazyVec<I, T, I, S1T>,
|
||||
pub pct90: LazyVec<I, T, I, S1T>,
|
||||
}
|
||||
|
||||
impl<T, S1T> LazyDistribution<Height, T, S1T>
|
||||
@@ -35,37 +35,37 @@ where
|
||||
) -> Self {
|
||||
let s = DistributionStats::<()>::SUFFIXES;
|
||||
Self {
|
||||
min: LazyVecFrom1::transformed::<F>(
|
||||
min: LazyVec::transformed::<F>(
|
||||
&format!("{name}_{}", s[0]),
|
||||
version,
|
||||
source.min.height.read_only_boxed_clone(),
|
||||
),
|
||||
max: LazyVecFrom1::transformed::<F>(
|
||||
max: LazyVec::transformed::<F>(
|
||||
&format!("{name}_{}", s[1]),
|
||||
version,
|
||||
source.max.height.read_only_boxed_clone(),
|
||||
),
|
||||
pct10: LazyVecFrom1::transformed::<F>(
|
||||
pct10: LazyVec::transformed::<F>(
|
||||
&format!("{name}_{}", s[2]),
|
||||
version,
|
||||
source.pct10.height.read_only_boxed_clone(),
|
||||
),
|
||||
pct25: LazyVecFrom1::transformed::<F>(
|
||||
pct25: LazyVec::transformed::<F>(
|
||||
&format!("{name}_{}", s[3]),
|
||||
version,
|
||||
source.pct25.height.read_only_boxed_clone(),
|
||||
),
|
||||
median: LazyVecFrom1::transformed::<F>(
|
||||
median: LazyVec::transformed::<F>(
|
||||
&format!("{name}_{}", s[4]),
|
||||
version,
|
||||
source.median.height.read_only_boxed_clone(),
|
||||
),
|
||||
pct75: LazyVecFrom1::transformed::<F>(
|
||||
pct75: LazyVec::transformed::<F>(
|
||||
&format!("{name}_{}", s[5]),
|
||||
version,
|
||||
source.pct75.height.read_only_boxed_clone(),
|
||||
),
|
||||
pct90: LazyVecFrom1::transformed::<F>(
|
||||
pct90: LazyVec::transformed::<F>(
|
||||
&format!("{name}_{}", s[6]),
|
||||
version,
|
||||
source.pct90.height.read_only_boxed_clone(),
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Dollars, Height, Version};
|
||||
use vecdb::{LazyVecFrom1, ReadableCloneableVec};
|
||||
use vecdb::{LazyVec, ReadableCloneableVec};
|
||||
|
||||
use crate::internal::{FiatPerBlock, FiatType, LazyPreviousDeltaVec};
|
||||
|
||||
/// Per-block fiat data derived from stored cumulative cents.
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct LazyFiatBlock<C: FiatType> {
|
||||
pub usd: LazyVecFrom1<Height, Dollars, Height, C>,
|
||||
pub usd: LazyVec<Height, Dollars, Height, C>,
|
||||
pub cents: LazyPreviousDeltaVec<Height, C>,
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ impl<C: FiatType> LazyFiatBlock<C> {
|
||||
cumulative.cents.height.read_only_boxed_clone(),
|
||||
);
|
||||
let usd =
|
||||
LazyVecFrom1::transformed::<C::ToDollars>(name, version, cents.read_only_boxed_clone());
|
||||
LazyVec::transformed::<C::ToDollars>(name, version, cents.read_only_boxed_clone());
|
||||
|
||||
Self { usd, cents }
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Dollars, Height, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use vecdb::{DeltaSub, LazyDeltaVec, LazyVecFrom1, ReadOnlyClone, ReadableCloneableVec};
|
||||
use vecdb::{DeltaSub, LazyDeltaVec, LazyVec, ReadOnlyClone, ReadableCloneableVec};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
@@ -55,7 +55,7 @@ impl<C: FiatType> LazyRollingSumsFiatFromHeight<C> {
|
||||
};
|
||||
|
||||
let usd = LazyPerBlock {
|
||||
height: LazyVecFrom1::transformed::<C::ToDollars>(
|
||||
height: LazyVec::transformed::<C::ToDollars>(
|
||||
&full_name,
|
||||
version,
|
||||
cents.height.read_only_boxed_clone(),
|
||||
|
||||
@@ -3,7 +3,7 @@ use brk_types::{Height, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{
|
||||
LazyVecFrom1, PcoVecValue, ReadOnlyClone, ReadableBoxedVec, ReadableCloneableVec, ReadableVec,
|
||||
LazyVec, PcoVecValue, ReadOnlyClone, ReadableBoxedVec, ReadableCloneableVec, ReadableVec,
|
||||
TypedVec, UnaryTransform, VecValue,
|
||||
};
|
||||
|
||||
@@ -21,7 +21,7 @@ where
|
||||
T: VecValue + PartialOrd + JsonSchema,
|
||||
S1T: VecValue,
|
||||
{
|
||||
pub height: LazyVecFrom1<Height, T, Height, S1T>,
|
||||
pub height: LazyVec<Height, T, Height, S1T>,
|
||||
#[deref]
|
||||
#[deref_mut]
|
||||
#[traversable(flatten)]
|
||||
@@ -40,7 +40,7 @@ where
|
||||
resolutions: &Resolutions<S1T>,
|
||||
) -> Self {
|
||||
Self {
|
||||
height: LazyVecFrom1::transformed::<F>(name, version, height_source),
|
||||
height: LazyVec::transformed::<F>(name, version, height_source),
|
||||
resolutions: Box::new(DerivedResolutions::from_derived_computed::<F>(
|
||||
name,
|
||||
version,
|
||||
@@ -84,11 +84,7 @@ where
|
||||
V: TypedVec<I = Height, T = S1T> + ReadableVec<Height, S1T> + Clone + 'static,
|
||||
{
|
||||
Self {
|
||||
height: LazyVecFrom1::transformed::<F>(
|
||||
name,
|
||||
version,
|
||||
height_source.read_only_boxed_clone(),
|
||||
),
|
||||
height: LazyVec::transformed::<F>(name, version, height_source.read_only_boxed_clone()),
|
||||
resolutions: Box::new(DerivedResolutions::from_height_source::<F, V>(
|
||||
name,
|
||||
version,
|
||||
@@ -114,11 +110,7 @@ where
|
||||
Resolutions::forced_import_uncached(name, height_source.clone(), version, indexes);
|
||||
|
||||
Self {
|
||||
height: LazyVecFrom1::transformed::<F>(
|
||||
name,
|
||||
version,
|
||||
height_source.read_only_boxed_clone(),
|
||||
),
|
||||
height: LazyVec::transformed::<F>(name, version, height_source.read_only_boxed_clone()),
|
||||
resolutions: Box::new(DerivedResolutions::from_derived_computed::<F>(
|
||||
name,
|
||||
version,
|
||||
@@ -138,11 +130,7 @@ where
|
||||
S2T: ComputedVecValue + JsonSchema,
|
||||
{
|
||||
Self {
|
||||
height: LazyVecFrom1::transformed::<F>(
|
||||
name,
|
||||
version,
|
||||
source.height.read_only_boxed_clone(),
|
||||
),
|
||||
height: LazyVec::transformed::<F>(name, version, source.height.read_only_boxed_clone()),
|
||||
resolutions: Box::new(DerivedResolutions::from_lazy::<F, S2T>(
|
||||
name,
|
||||
version,
|
||||
@@ -167,7 +155,7 @@ where
|
||||
where
|
||||
S: VecValue,
|
||||
{
|
||||
let indexed = LazyVecFrom1::init(
|
||||
let indexed = LazyVec::init(
|
||||
&format!("{name}_source"),
|
||||
version,
|
||||
source.read_only_boxed_clone(),
|
||||
@@ -188,7 +176,7 @@ where
|
||||
where
|
||||
S: VecValue,
|
||||
{
|
||||
let indexed = LazyVecFrom1::init(
|
||||
let indexed = LazyVec::init(
|
||||
&format!("{name}_source"),
|
||||
version,
|
||||
source.read_only_boxed_clone(),
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
use brk_traversable::Traversable;
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{LazyVecFrom1, ReadableBoxedVec, UnaryTransform, VecIndex, VecValue};
|
||||
use vecdb::{LazyVec, ReadableBoxedVec, UnaryTransform, VecIndex, VecValue};
|
||||
|
||||
use brk_types::Version;
|
||||
|
||||
#[derive(Clone, Deref, DerefMut, Traversable)]
|
||||
#[traversable(transparent)]
|
||||
pub struct LazyTransformLast<I, T, S1T = T>(pub LazyVecFrom1<I, T, I, S1T>)
|
||||
pub struct LazyTransformLast<I, T, S1T = T>(pub LazyVec<I, T, I, S1T>)
|
||||
where
|
||||
I: VecIndex,
|
||||
T: VecValue + PartialOrd + JsonSchema,
|
||||
@@ -24,6 +24,6 @@ where
|
||||
version: Version,
|
||||
source: ReadableBoxedVec<I, S1T>,
|
||||
) -> Self {
|
||||
Self(LazyVecFrom1::transformed::<F>(name, version, source))
|
||||
Self(LazyVec::transformed::<F>(name, version, source))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, StoredF32, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use vecdb::{LazyVecFrom1, ReadableCloneableVec, VecValue};
|
||||
use vecdb::{LazyVec, ReadableCloneableVec, VecValue};
|
||||
|
||||
use crate::internal::{FixedRatio, Percent};
|
||||
|
||||
@@ -10,7 +10,7 @@ use crate::internal::{FixedRatio, Percent};
|
||||
#[traversable(transparent)]
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub struct LazyPercentVec<B: FixedRatio, S: VecValue>(
|
||||
pub Percent<LazyVecFrom1<Height, B, Height, S>, LazyVecFrom1<Height, StoredF32, Height, B>>,
|
||||
pub Percent<LazyVec<Height, B, Height, S>, LazyVec<Height, StoredF32, Height, B>>,
|
||||
);
|
||||
|
||||
impl<B: FixedRatio, S: VecValue> LazyPercentVec<B, S> {
|
||||
@@ -20,19 +20,19 @@ impl<B: FixedRatio, S: VecValue> LazyPercentVec<B, S> {
|
||||
source: &(impl ReadableCloneableVec<Height, S> + 'static),
|
||||
compute: fn(Height, S) -> B,
|
||||
) -> Self {
|
||||
let ppm = LazyVecFrom1::init(
|
||||
let ppm = LazyVec::init(
|
||||
&format!("{name}_{}", B::SUFFIX),
|
||||
version,
|
||||
source.read_only_boxed_clone(),
|
||||
compute,
|
||||
);
|
||||
let ppm_source = ppm.read_only_boxed_clone();
|
||||
let ratio = LazyVecFrom1::transformed::<B::ToRatio>(
|
||||
let ratio = LazyVec::transformed::<B::ToRatio>(
|
||||
&format!("{name}_ratio"),
|
||||
version,
|
||||
ppm_source.clone(),
|
||||
);
|
||||
let percent = LazyVecFrom1::transformed::<B::ToPercent>(name, version, ppm_source);
|
||||
let percent = LazyVec::transformed::<B::ToPercent>(name, version, ppm_source);
|
||||
|
||||
Self(Percent {
|
||||
ppm,
|
||||
|
||||
@@ -3,8 +3,7 @@ use brk_types::{Bitcoin, Dollars, Height, StoredF32, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{
|
||||
DeltaChange, DeltaRate, LazyDeltaVec, LazyVecFrom1, ReadOnlyClone, ReadableCloneableVec,
|
||||
VecValue,
|
||||
DeltaChange, DeltaRate, LazyDeltaVec, LazyVec, ReadOnlyClone, ReadableCloneableVec, VecValue,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
@@ -117,7 +116,7 @@ where
|
||||
|
||||
let rate_ratio_name = format!("{full_name}_rate_ratio");
|
||||
let ratio = LazyPerBlock {
|
||||
height: LazyVecFrom1::transformed::<B::ToRatio>(
|
||||
height: LazyVec::transformed::<B::ToRatio>(
|
||||
&rate_ratio_name,
|
||||
version,
|
||||
ppm.height.read_only_boxed_clone(),
|
||||
@@ -131,7 +130,7 @@ where
|
||||
|
||||
let rate_name = format!("{full_name}_rate");
|
||||
let percent = LazyPerBlock {
|
||||
height: LazyVecFrom1::transformed::<B::ToPercent>(
|
||||
height: LazyVec::transformed::<B::ToPercent>(
|
||||
&rate_name,
|
||||
version,
|
||||
ppm.height.read_only_boxed_clone(),
|
||||
@@ -231,7 +230,7 @@ where
|
||||
|
||||
// Absolute change (btc): lazy from sats delta
|
||||
let btc = LazyPerBlock {
|
||||
height: LazyVecFrom1::transformed::<C::ToBitcoin>(
|
||||
height: LazyVec::transformed::<C::ToBitcoin>(
|
||||
&full_name,
|
||||
version,
|
||||
sats.height.read_only_boxed_clone(),
|
||||
@@ -263,7 +262,7 @@ where
|
||||
|
||||
let rate_ratio_name = format!("{full_name}_rate_ratio");
|
||||
let ratio = LazyPerBlock {
|
||||
height: LazyVecFrom1::transformed::<B::ToRatio>(
|
||||
height: LazyVec::transformed::<B::ToRatio>(
|
||||
&rate_ratio_name,
|
||||
version,
|
||||
ppm.height.read_only_boxed_clone(),
|
||||
@@ -277,7 +276,7 @@ where
|
||||
|
||||
let rate_name = format!("{full_name}_rate");
|
||||
let percent = LazyPerBlock {
|
||||
height: LazyVecFrom1::transformed::<B::ToPercent>(
|
||||
height: LazyVec::transformed::<B::ToPercent>(
|
||||
&rate_name,
|
||||
version,
|
||||
ppm.height.read_only_boxed_clone(),
|
||||
@@ -376,7 +375,7 @@ where
|
||||
|
||||
// Absolute change (usd): lazy from cents delta
|
||||
let usd = LazyPerBlock {
|
||||
height: LazyVecFrom1::transformed::<C::ToDollars>(
|
||||
height: LazyVec::transformed::<C::ToDollars>(
|
||||
&full_name,
|
||||
version,
|
||||
cents.height.read_only_boxed_clone(),
|
||||
@@ -408,7 +407,7 @@ where
|
||||
|
||||
let rate_ratio_name = format!("{full_name}_rate_ratio");
|
||||
let ratio = LazyPerBlock {
|
||||
height: LazyVecFrom1::transformed::<B::ToRatio>(
|
||||
height: LazyVec::transformed::<B::ToRatio>(
|
||||
&rate_ratio_name,
|
||||
version,
|
||||
ppm.height.read_only_boxed_clone(),
|
||||
@@ -422,7 +421,7 @@ where
|
||||
|
||||
let rate_name = format!("{full_name}_rate");
|
||||
let percent = LazyPerBlock {
|
||||
height: LazyVecFrom1::transformed::<B::ToPercent>(
|
||||
height: LazyVec::transformed::<B::ToPercent>(
|
||||
&rate_name,
|
||||
version,
|
||||
ppm.height.read_only_boxed_clone(),
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Bitcoin, Cents, Dollars, Height, Sats, Version};
|
||||
use vecdb::{LazyVecFrom1, ReadableCloneableVec};
|
||||
use vecdb::{LazyVec, ReadableCloneableVec};
|
||||
|
||||
use crate::internal::{CentsUnsignedToDollars, LazyPreviousDeltaVec, SatsToBitcoin, ValuePerBlock};
|
||||
|
||||
/// Per-block amount data derived from stored cumulative sats and cents.
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct LazyValueBlock {
|
||||
pub btc: LazyVecFrom1<Height, Bitcoin, Height, Sats>,
|
||||
pub btc: LazyVec<Height, Bitcoin, Height, Sats>,
|
||||
pub sats: LazyPreviousDeltaVec<Height, Sats>,
|
||||
pub usd: LazyVecFrom1<Height, Dollars, Height, Cents>,
|
||||
pub usd: LazyVec<Height, Dollars, Height, Cents>,
|
||||
pub cents: LazyPreviousDeltaVec<Height, Cents>,
|
||||
}
|
||||
|
||||
@@ -39,13 +39,13 @@ impl LazyValueBlock {
|
||||
cumulative_sats.read_only_boxed_clone(),
|
||||
);
|
||||
let btc =
|
||||
LazyVecFrom1::transformed::<SatsToBitcoin>(name, version, sats.read_only_boxed_clone());
|
||||
LazyVec::transformed::<SatsToBitcoin>(name, version, sats.read_only_boxed_clone());
|
||||
let cents = LazyPreviousDeltaVec::new(
|
||||
&format!("{name}_cents"),
|
||||
version,
|
||||
cumulative_cents.read_only_boxed_clone(),
|
||||
);
|
||||
let usd = LazyVecFrom1::transformed::<CentsUnsignedToDollars>(
|
||||
let usd = LazyVec::transformed::<CentsUnsignedToDollars>(
|
||||
&format!("{name}_usd"),
|
||||
version,
|
||||
cents.read_only_boxed_clone(),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Bitcoin, Cents, Dollars, Height, Sats, StoredF32, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use vecdb::{DeltaAvg, LazyDeltaVec, LazyVecFrom1, ReadOnlyClone, ReadableCloneableVec};
|
||||
use vecdb::{DeltaAvg, LazyDeltaVec, LazyVec, ReadOnlyClone, ReadableCloneableVec};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
@@ -66,7 +66,7 @@ impl LazyRollingAvgsAmountFromHeight {
|
||||
|
||||
// Btc: f64 sats avg / 1e8
|
||||
let btc = LazyPerBlock {
|
||||
height: LazyVecFrom1::transformed::<AvgSatsToBtc>(
|
||||
height: LazyVec::transformed::<AvgSatsToBtc>(
|
||||
&full_name,
|
||||
version,
|
||||
sats.height.read_only_boxed_clone(),
|
||||
@@ -99,7 +99,7 @@ impl LazyRollingAvgsAmountFromHeight {
|
||||
|
||||
// Usd: f64 cents avg / 100
|
||||
let usd = LazyPerBlock {
|
||||
height: LazyVecFrom1::transformed::<AvgCentsToUsd>(
|
||||
height: LazyVec::transformed::<AvgCentsToUsd>(
|
||||
&format!("{full_name}_usd"),
|
||||
version,
|
||||
cents.height.read_only_boxed_clone(),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Bitcoin, Cents, Dollars, Height, Sats, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use vecdb::{DeltaSub, LazyDeltaVec, LazyVecFrom1, ReadOnlyClone, ReadableCloneableVec};
|
||||
use vecdb::{DeltaSub, LazyDeltaVec, LazyVec, ReadOnlyClone, ReadableCloneableVec};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
@@ -66,7 +66,7 @@ impl LazyRollingSumsAmountFromHeight {
|
||||
|
||||
// Btc lazy from sats
|
||||
let btc = LazyPerBlock {
|
||||
height: LazyVecFrom1::transformed::<SatsToBitcoin>(
|
||||
height: LazyVec::transformed::<SatsToBitcoin>(
|
||||
&full_name,
|
||||
version,
|
||||
sats.height.read_only_boxed_clone(),
|
||||
@@ -99,7 +99,7 @@ impl LazyRollingSumsAmountFromHeight {
|
||||
|
||||
// Usd lazy from cents
|
||||
let usd = LazyPerBlock {
|
||||
height: LazyVecFrom1::transformed::<CentsUnsignedToDollars>(
|
||||
height: LazyVec::transformed::<CentsUnsignedToDollars>(
|
||||
&format!("{full_name}_usd"),
|
||||
version,
|
||||
cents.height.read_only_boxed_clone(),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{TxIndex, Version};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{LazyVecFrom1, UnaryTransform};
|
||||
use vecdb::{LazyVec, UnaryTransform};
|
||||
|
||||
use crate::internal::{ComputedVecValue, LazyTxDerivedDistribution, TxDerivedDistribution};
|
||||
|
||||
@@ -13,7 +13,7 @@ where
|
||||
S: ComputedVecValue,
|
||||
DSource: ComputedVecValue,
|
||||
{
|
||||
pub tx_index: LazyVecFrom1<TxIndex, T, TxIndex, S>,
|
||||
pub tx_index: LazyVec<TxIndex, T, TxIndex, S>,
|
||||
#[traversable(flatten)]
|
||||
pub distribution: LazyTxDerivedDistribution<T, DSource>,
|
||||
}
|
||||
@@ -27,7 +27,7 @@ where
|
||||
pub(crate) fn new<F: UnaryTransform<DSource, T>>(
|
||||
name: &str,
|
||||
version: Version,
|
||||
tx_index: LazyVecFrom1<TxIndex, T, TxIndex, S>,
|
||||
tx_index: LazyVec<TxIndex, T, TxIndex, S>,
|
||||
source_distribution: &TxDerivedDistribution<DSource>,
|
||||
) -> Self {
|
||||
let distribution =
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Bitcoin, Cents, Dollars, Height, Sats, Version};
|
||||
use vecdb::{LazyVecFrom1, ReadableCloneableVec, UnaryTransform, VecIndex};
|
||||
use vecdb::{LazyVec, ReadableCloneableVec, UnaryTransform, VecIndex};
|
||||
|
||||
use crate::internal::SpotValuePerBlock;
|
||||
|
||||
@@ -9,10 +9,10 @@ use crate::internal::SpotValuePerBlock;
|
||||
/// All fields are lazy transforms from existing sources - no storage.
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct LazyValue<I: VecIndex> {
|
||||
pub btc: LazyVecFrom1<I, Bitcoin, I, Sats>,
|
||||
pub sats: LazyVecFrom1<I, Sats, I, Sats>,
|
||||
pub usd: LazyVecFrom1<I, Dollars, I, Dollars>,
|
||||
pub cents: LazyVecFrom1<I, Cents, I, Cents>,
|
||||
pub btc: LazyVec<I, Bitcoin, I, Sats>,
|
||||
pub sats: LazyVec<I, Sats, I, Sats>,
|
||||
pub usd: LazyVec<I, Dollars, I, Dollars>,
|
||||
pub cents: LazyVec<I, Cents, I, Cents>,
|
||||
}
|
||||
|
||||
impl LazyValue<Height> {
|
||||
@@ -32,25 +32,25 @@ impl LazyValue<Height> {
|
||||
CentsTransform: UnaryTransform<Cents, Cents>,
|
||||
DollarsTransform: UnaryTransform<Dollars, Dollars>,
|
||||
{
|
||||
let sats = LazyVecFrom1::transformed::<SatsTransform>(
|
||||
let sats = LazyVec::transformed::<SatsTransform>(
|
||||
&format!("{name}_sats"),
|
||||
version,
|
||||
source.sats.height.read_only_boxed_clone(),
|
||||
);
|
||||
|
||||
let btc = LazyVecFrom1::transformed::<BitcoinTransform>(
|
||||
let btc = LazyVec::transformed::<BitcoinTransform>(
|
||||
name,
|
||||
version,
|
||||
source.sats.height.read_only_boxed_clone(),
|
||||
);
|
||||
|
||||
let cents = LazyVecFrom1::transformed::<CentsTransform>(
|
||||
let cents = LazyVec::transformed::<CentsTransform>(
|
||||
&format!("{name}_cents"),
|
||||
version,
|
||||
source.cents.height.read_only_boxed_clone(),
|
||||
);
|
||||
|
||||
let usd = LazyVecFrom1::transformed::<DollarsTransform>(
|
||||
let usd = LazyVec::transformed::<DollarsTransform>(
|
||||
&format!("{name}_usd"),
|
||||
version,
|
||||
source.usd.height.read_only_boxed_clone(),
|
||||
|
||||
@@ -469,6 +469,7 @@ impl Computer {
|
||||
&self.price,
|
||||
&self.distribution,
|
||||
&self.frameworks,
|
||||
&self.market.moving_average,
|
||||
exit,
|
||||
)
|
||||
})?;
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
//! custom daily repeat/last-day views: cents are stored, USD is derived from cents,
|
||||
//! and sats are derived from USD.
|
||||
|
||||
use brk_types::{Cents, Day1, Dollars, SatsFract, Version};
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Cents, Dollars, SatsFract, Version};
|
||||
use vecdb::{ReadableCloneableVec, Rw, StorageMode};
|
||||
|
||||
use crate::internal::{
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
use std::collections::VecDeque;
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_types::{CapitalSentimentPhase, Cents, Day1, StoredBool, StoredU8, Version};
|
||||
use brk_types::{CapitalSentimentPhase, Cents, Day1, Height, StoredBool, StoredU8, Version};
|
||||
use vecdb::{AnyStoredVec, AnyVec, Exit, ReadableVec, VecIndex, WritableVec};
|
||||
|
||||
use super::Vecs;
|
||||
use crate::{
|
||||
distribution, indexes, internal::db_utils::validate_any_computed_version_or_reset, price,
|
||||
distribution, indexes, internal::db_utils::validate_any_computed_version_or_reset, market,
|
||||
price,
|
||||
};
|
||||
|
||||
const PRICE_SMA_DAYS: usize = 365;
|
||||
const WRITE_INTERVAL_DAYS: usize = 1_000;
|
||||
|
||||
impl Vecs {
|
||||
@@ -20,9 +18,11 @@ impl Vecs {
|
||||
indexes: &indexes::Vecs,
|
||||
prices: &price::Vecs,
|
||||
distribution: &distribution::Vecs,
|
||||
moving_average: &market::MovingAverageVecs,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
let close = &prices.split.close.cents.day1;
|
||||
let spot = &prices.spot.cents.height;
|
||||
let sma = &moving_average.sma._1y.cents.height;
|
||||
let all = &distribution
|
||||
.utxo_cohorts
|
||||
.all
|
||||
@@ -31,7 +31,7 @@ impl Vecs {
|
||||
.capitalized
|
||||
.price
|
||||
.cents
|
||||
.day1;
|
||||
.height;
|
||||
let sth = &distribution
|
||||
.utxo_cohorts
|
||||
.sth
|
||||
@@ -40,7 +40,7 @@ impl Vecs {
|
||||
.capitalized
|
||||
.price
|
||||
.cents
|
||||
.day1;
|
||||
.height;
|
||||
let lth = &distribution
|
||||
.utxo_cohorts
|
||||
.lth
|
||||
@@ -49,24 +49,28 @@ impl Vecs {
|
||||
.capitalized
|
||||
.price
|
||||
.cents
|
||||
.day1;
|
||||
.height;
|
||||
let first_height = &indexes.day1.first_height;
|
||||
|
||||
let source_version: Version = [close.version(), all.version(), sth.version(), lth.version()]
|
||||
let source_version: Version = [
|
||||
spot.version(),
|
||||
sma.version(),
|
||||
all.version(),
|
||||
sth.version(),
|
||||
lth.version(),
|
||||
first_height.version(),
|
||||
]
|
||||
.into_iter()
|
||||
.sum();
|
||||
validate_any_computed_version_or_reset(&mut self.phase_code.day1, source_version)?;
|
||||
validate_any_computed_version_or_reset(&mut self.is_long.day1, source_version)?;
|
||||
|
||||
let source_end = [
|
||||
indexes.day1.date.len(),
|
||||
close.len(),
|
||||
all.len(),
|
||||
sth.len(),
|
||||
lth.len(),
|
||||
]
|
||||
let height_end = [spot.len(), sma.len(), all.len(), sth.len(), lth.len()]
|
||||
.into_iter()
|
||||
.min()
|
||||
.unwrap_or_default();
|
||||
let first_heights = first_height.collect();
|
||||
let source_end = indexes.day1.date.len().min(first_heights.len());
|
||||
let recompute_from = recompute_day(indexer, indexes)
|
||||
.map(usize::from)
|
||||
.unwrap_or_default();
|
||||
@@ -85,28 +89,24 @@ impl Vecs {
|
||||
.map(Day1::from)
|
||||
.and_then(|day| self.is_long.day1.collect_one(day))
|
||||
.is_some_and(|value| value.is_true());
|
||||
let mut previous_over_sth = start
|
||||
.checked_sub(1)
|
||||
.map(Day1::from)
|
||||
.map(|day| {
|
||||
is_over_sth(
|
||||
close.collect_one(day).flatten(),
|
||||
sth.collect_one(day).flatten(),
|
||||
)
|
||||
});
|
||||
let mut sma = RollingSma::from_history(close, start);
|
||||
|
||||
let mut previous_over_sth = start.checked_sub(1).map(|day| {
|
||||
let height = last_height_of_day(&first_heights, day, height_end);
|
||||
is_over_sth(
|
||||
height.and_then(|height| spot.collect_one(height)),
|
||||
height.and_then(|height| sth.collect_one(height)),
|
||||
)
|
||||
});
|
||||
for day_index in start..source_end {
|
||||
let day = Day1::from(day_index);
|
||||
let price = close.collect_one(day).flatten();
|
||||
let sth = sth.collect_one(day).flatten();
|
||||
let over_sth = is_over_sth(price, sth);
|
||||
let height = last_height_of_day(&first_heights, day_index, height_end);
|
||||
let price = height.and_then(|height| spot.collect_one(height));
|
||||
let sth_price = height.and_then(|height| sth.collect_one(height));
|
||||
let over_sth = is_over_sth(price, sth_price);
|
||||
let code = classify_phase_code(
|
||||
price,
|
||||
all.collect_one(day).flatten(),
|
||||
sth,
|
||||
lth.collect_one(day).flatten(),
|
||||
sma.observe(price),
|
||||
height.and_then(|height| all.collect_one(height)),
|
||||
sth_price,
|
||||
height.and_then(|height| lth.collect_one(height)),
|
||||
height.and_then(|height| sma.collect_one(height)),
|
||||
);
|
||||
is_long = next_is_long(is_long, previous_over_sth, over_sth, code);
|
||||
|
||||
@@ -114,9 +114,7 @@ impl Vecs {
|
||||
self.is_long.day1.push(StoredBool::from(is_long));
|
||||
previous_over_sth = Some(over_sth);
|
||||
|
||||
if (day_index + 1).is_multiple_of(WRITE_INTERVAL_DAYS)
|
||||
|| day_index + 1 == source_end
|
||||
{
|
||||
if (day_index + 1).is_multiple_of(WRITE_INTERVAL_DAYS) || day_index + 1 == source_end {
|
||||
let _lock = exit.lock();
|
||||
self.phase_code.day1.write()?;
|
||||
self.is_long.day1.write()?;
|
||||
@@ -127,36 +125,14 @@ impl Vecs {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct RollingSma {
|
||||
values: VecDeque<u64>,
|
||||
sum: u128,
|
||||
}
|
||||
|
||||
impl RollingSma {
|
||||
fn from_history(
|
||||
source: &impl ReadableVec<Day1, Option<Cents>>,
|
||||
end: usize,
|
||||
) -> Self {
|
||||
let mut sma = Self::default();
|
||||
source.for_each_range_at(0, end, |price| {
|
||||
let _ = sma.observe(price);
|
||||
});
|
||||
sma
|
||||
}
|
||||
|
||||
/// Observe one daily close and return the sum of the latest 365 valid closes.
|
||||
fn observe(&mut self, price: Option<Cents>) -> Option<u128> {
|
||||
if let Some(price) = price.filter(|price| is_finite_positive(*price)) {
|
||||
let price = price.inner();
|
||||
self.values.push_back(price);
|
||||
self.sum += u128::from(price);
|
||||
if self.values.len() > PRICE_SMA_DAYS {
|
||||
self.sum -= u128::from(self.values.pop_front().unwrap());
|
||||
}
|
||||
}
|
||||
(self.values.len() == PRICE_SMA_DAYS).then_some(self.sum)
|
||||
}
|
||||
fn last_height_of_day(first_heights: &[Height], day: usize, height_end: usize) -> Option<Height> {
|
||||
let first = first_heights.get(day)?.to_usize().min(height_end);
|
||||
let end = first_heights
|
||||
.get(day + 1)
|
||||
.map(|height| height.to_usize())
|
||||
.unwrap_or(height_end)
|
||||
.min(height_end);
|
||||
(first < end).then(|| Height::from(end - 1))
|
||||
}
|
||||
|
||||
/// Advance the stateful short/long strategy used by BRK Signal.
|
||||
@@ -166,8 +142,7 @@ fn next_is_long(
|
||||
over_sth: bool,
|
||||
phase_code: StoredU8,
|
||||
) -> bool {
|
||||
let crossed_above_sth =
|
||||
previous_over_sth.is_some_and(|previous| !previous && over_sth);
|
||||
let crossed_above_sth = previous_over_sth.is_some_and(|previous| !previous && over_sth);
|
||||
|
||||
if !is_long && crossed_above_sth {
|
||||
return true;
|
||||
@@ -186,11 +161,9 @@ fn is_finite_positive(value: Cents) -> bool {
|
||||
|
||||
#[inline]
|
||||
fn is_over_sth(price: Option<Cents>, sth: Option<Cents>) -> bool {
|
||||
price
|
||||
.zip(sth)
|
||||
.is_some_and(|(price, sth)| {
|
||||
is_finite_positive(price) && is_finite_positive(sth) && price >= sth
|
||||
})
|
||||
price.zip(sth).is_some_and(|(price, sth)| {
|
||||
is_finite_positive(price) && is_finite_positive(sth) && price >= sth
|
||||
})
|
||||
}
|
||||
|
||||
/// Code `0` means the capitalized-price references are not all available yet.
|
||||
@@ -199,7 +172,7 @@ fn classify_phase_code(
|
||||
all: Option<Cents>,
|
||||
sth: Option<Cents>,
|
||||
lth: Option<Cents>,
|
||||
sma_sum: Option<u128>,
|
||||
sma: Option<Cents>,
|
||||
) -> StoredU8 {
|
||||
let Some((price, all, sth, lth)) = price
|
||||
.zip(all)
|
||||
@@ -215,26 +188,24 @@ fn classify_phase_code(
|
||||
return StoredU8::ZERO;
|
||||
};
|
||||
|
||||
StoredU8::new(classify_phase(price, all, sth, lth, sma_sum).code())
|
||||
StoredU8::new(classify_phase(price, all, sth, lth, sma).code())
|
||||
}
|
||||
|
||||
/// Classify investor sentiment from the three capitalized-price references,
|
||||
/// using the 365-daily-close SMA only as confirmation and disambiguation.
|
||||
/// using the one-year price SMA only as confirmation and disambiguation.
|
||||
fn classify_phase(
|
||||
price: Cents,
|
||||
all: Cents,
|
||||
sth: Cents,
|
||||
lth: Cents,
|
||||
sma_sum: Option<u128>,
|
||||
sma: Option<Cents>,
|
||||
) -> CapitalSentimentPhase {
|
||||
use CapitalSentimentPhase as Phase;
|
||||
|
||||
let price_x_days = price.as_u128() * PRICE_SMA_DAYS as u128;
|
||||
let all_x_days = all.as_u128() * PRICE_SMA_DAYS as u128;
|
||||
let above_all = price >= all;
|
||||
let above_sth = price >= sth;
|
||||
let above_lth = price >= lth;
|
||||
let above_sma = sma_sum.is_some_and(|sma| price_x_days >= sma);
|
||||
let above_sma = sma.is_some_and(|sma| price >= sma);
|
||||
let bull_structure = sth > lth;
|
||||
let above_slow_refs = above_all && above_lth;
|
||||
let above_any_slow_ref = above_all || above_lth;
|
||||
@@ -242,9 +213,9 @@ fn classify_phase(
|
||||
.into_iter()
|
||||
.filter(|reference| *reference > price)
|
||||
.count()
|
||||
+ usize::from(sma_sum.is_some_and(|sma| sma > price_x_days));
|
||||
+ usize::from(sma.is_some_and(|sma| sma > price));
|
||||
let price_in_middle = references_above_price == 2;
|
||||
let core_bull_phase = if sma_sum.is_some_and(|sma| all_x_days > sma) {
|
||||
let core_bull_phase = if sma.is_some_and(|sma| all > sma) {
|
||||
Phase::RagingBull
|
||||
} else {
|
||||
Phase::Bull
|
||||
@@ -255,7 +226,7 @@ fn classify_phase(
|
||||
Phase::Bear
|
||||
};
|
||||
|
||||
if sma_sum.is_none() {
|
||||
if sma.is_none() {
|
||||
if bull_structure {
|
||||
if above_sth {
|
||||
return if above_slow_refs {
|
||||
@@ -338,13 +309,43 @@ mod tests {
|
||||
Cents::new(value)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn samples_each_days_last_available_block() {
|
||||
let first_heights = [0_usize, 2, 2, 5].map(Height::from);
|
||||
|
||||
assert_eq!(
|
||||
last_height_of_day(&first_heights, 0, 7),
|
||||
Some(Height::from(1_usize))
|
||||
);
|
||||
assert_eq!(last_height_of_day(&first_heights, 1, 7), None);
|
||||
assert_eq!(
|
||||
last_height_of_day(&first_heights, 2, 7),
|
||||
Some(Height::from(4_usize))
|
||||
);
|
||||
assert_eq!(
|
||||
last_height_of_day(&first_heights, 3, 7),
|
||||
Some(Height::from(6_usize))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sampling_clamps_the_current_day_to_the_shared_source_length() {
|
||||
let first_heights = [0_usize, 2, 5].map(Height::from);
|
||||
|
||||
assert_eq!(
|
||||
last_height_of_day(&first_heights, 1, 4),
|
||||
Some(Height::from(3_usize))
|
||||
);
|
||||
assert_eq!(last_height_of_day(&first_heights, 2, 4), None);
|
||||
}
|
||||
|
||||
fn classify(price: u64, all: u64, sth: u64, lth: u64, sma: u64) -> CapitalSentimentPhase {
|
||||
classify_phase(
|
||||
cents(price),
|
||||
cents(all),
|
||||
cents(sth),
|
||||
cents(lth),
|
||||
Some(u128::from(sma) * PRICE_SMA_DAYS as u128),
|
||||
Some(cents(sma)),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -407,14 +408,14 @@ mod tests {
|
||||
Some(cents(70)),
|
||||
Some(cents(80)),
|
||||
None,
|
||||
Some(u128::from(50_u64) * PRICE_SMA_DAYS as u128),
|
||||
Some(cents(50)),
|
||||
),
|
||||
StoredU8::ZERO
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn phase_is_available_before_the_sma_window_is_full() {
|
||||
fn phase_is_available_without_sma() {
|
||||
assert_eq!(
|
||||
classify_phase(cents(100), cents(70), cents(80), cents(60), None),
|
||||
CapitalSentimentPhase::Bull
|
||||
|
||||
@@ -8,7 +8,7 @@ use crate::{
|
||||
internal::{DailyMappings, DailyMetric, LazyDailyMetric},
|
||||
};
|
||||
|
||||
const VERSION: Version = Version::new(4);
|
||||
const VERSION: Version = Version::new(5);
|
||||
|
||||
struct CodeToPhase;
|
||||
|
||||
@@ -53,12 +53,8 @@ impl Vecs {
|
||||
let version = parent_version + VERSION;
|
||||
let mappings = DailyMappings::new(indexes);
|
||||
|
||||
let phase_code = DailyMetric::forced_import(
|
||||
db,
|
||||
"capital_sentiment_phase_code",
|
||||
version,
|
||||
&mappings,
|
||||
)?;
|
||||
let phase_code =
|
||||
DailyMetric::forced_import(db, "capital_sentiment_phase_code", version, &mappings)?;
|
||||
let is_long = DailyMetric::<StoredBool>::forced_import(
|
||||
db,
|
||||
"capital_sentiment_is_long",
|
||||
|
||||
@@ -3,7 +3,7 @@ use brk_indexer::Indexer;
|
||||
use vecdb::Exit;
|
||||
|
||||
use super::Vecs;
|
||||
use crate::{distribution, frameworks, indexes, price};
|
||||
use crate::{distribution, frameworks, indexes, market, price};
|
||||
|
||||
impl Vecs {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
@@ -14,6 +14,7 @@ impl Vecs {
|
||||
prices: &price::Vecs,
|
||||
distribution: &distribution::Vecs,
|
||||
frameworks: &frameworks::Vecs,
|
||||
moving_average: &market::MovingAverageVecs,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.db.sync_bg_tasks()?;
|
||||
@@ -26,8 +27,14 @@ impl Vecs {
|
||||
&frameworks.coinflow,
|
||||
exit,
|
||||
)?;
|
||||
self.capital_sentiment
|
||||
.compute(indexer, indexes, prices, distribution, exit)?;
|
||||
self.capital_sentiment.compute(
|
||||
indexer,
|
||||
indexes,
|
||||
prices,
|
||||
distribution,
|
||||
moving_average,
|
||||
exit,
|
||||
)?;
|
||||
self.rarity_meter.compute(
|
||||
indexer,
|
||||
distribution,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use brk_types::{Cents, CentsCompact, Height, PartsPerMillion32, Version};
|
||||
use vecdb::{
|
||||
AnyVec, BinaryTransform, CachedReadableVec, CachedVec, LazyVecFrom1, ReadableCloneableVec,
|
||||
VecIndex,
|
||||
AnyVec, BinaryTransform, CachedReadableVec, CachedVec, LazyVec, ReadableCloneableVec, VecIndex,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
@@ -11,7 +10,7 @@ use crate::{
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(super) struct CachedComponentPrice {
|
||||
cache: CachedVec<LazyVecFrom1<Height, CentsCompact, Height, Cents>>,
|
||||
cache: CachedVec<LazyVec<Height, CentsCompact, Height, Cents>>,
|
||||
}
|
||||
|
||||
impl CachedComponentPrice {
|
||||
@@ -20,7 +19,7 @@ impl CachedComponentPrice {
|
||||
version: Version,
|
||||
source: &(impl ReadableCloneableVec<Height, Cents> + 'static),
|
||||
) -> Self {
|
||||
let compact = LazyVecFrom1::init(
|
||||
let compact = LazyVec::init(
|
||||
&format!("{name}_cached_price"),
|
||||
version,
|
||||
source.read_only_boxed_clone(),
|
||||
|
||||
@@ -243,9 +243,9 @@ impl Vecs {
|
||||
|
||||
txout_cursor.advance(block_first_tx - txout_cursor.position());
|
||||
tx_starts.clear();
|
||||
for _ in 0..tx_count {
|
||||
tx_starts.push(txout_cursor.next().unwrap().to_usize());
|
||||
}
|
||||
txout_cursor.for_each(tx_count, |txout_index| {
|
||||
tx_starts.push(txout_index.to_usize());
|
||||
});
|
||||
let out_start = tx_starts.first().copied().unwrap_or(out_end);
|
||||
|
||||
indexer
|
||||
|
||||
@@ -7,7 +7,7 @@ use derive_more::{Deref, DerefMut};
|
||||
use schemars::JsonSchema;
|
||||
use serde::Serialize;
|
||||
use vecdb::{
|
||||
BytesVecValue, CachedBoxedVec, Formattable, LazyVecFrom1, ReadableCloneableVec, UnaryTransform,
|
||||
BytesVecValue, CachedBoxedVec, Formattable, LazyVec, ReadableCloneableVec, UnaryTransform,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
@@ -88,7 +88,7 @@ where
|
||||
) -> Self {
|
||||
macro_rules! period {
|
||||
($idx:ident) => {
|
||||
LazyVecFrom1::transformed::<Transform>(
|
||||
LazyVec::transformed::<Transform>(
|
||||
name,
|
||||
version,
|
||||
source.$idx.read_only_boxed_clone(),
|
||||
@@ -121,21 +121,21 @@ where
|
||||
pub struct LazyOhlcVecs<T, S>(
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub PerResolution<
|
||||
LazyVecFrom1<Minute10, T, Minute10, S>,
|
||||
LazyVecFrom1<Minute30, T, Minute30, S>,
|
||||
LazyVecFrom1<Hour1, T, Hour1, S>,
|
||||
LazyVecFrom1<Hour4, T, Hour4, S>,
|
||||
LazyVecFrom1<Hour12, T, Hour12, S>,
|
||||
LazyVecFrom1<Day1, T, Day1, S>,
|
||||
LazyVecFrom1<Day3, T, Day3, S>,
|
||||
LazyVecFrom1<Week1, T, Week1, S>,
|
||||
LazyVecFrom1<Month1, T, Month1, S>,
|
||||
LazyVecFrom1<Month3, T, Month3, S>,
|
||||
LazyVecFrom1<Month6, T, Month6, S>,
|
||||
LazyVecFrom1<Year1, T, Year1, S>,
|
||||
LazyVecFrom1<Year10, T, Year10, S>,
|
||||
LazyVecFrom1<Halving, T, Halving, S>,
|
||||
LazyVecFrom1<Epoch, T, Epoch, S>,
|
||||
LazyVec<Minute10, T, Minute10, S>,
|
||||
LazyVec<Minute30, T, Minute30, S>,
|
||||
LazyVec<Hour1, T, Hour1, S>,
|
||||
LazyVec<Hour4, T, Hour4, S>,
|
||||
LazyVec<Hour12, T, Hour12, S>,
|
||||
LazyVec<Day1, T, Day1, S>,
|
||||
LazyVec<Day3, T, Day3, S>,
|
||||
LazyVec<Week1, T, Week1, S>,
|
||||
LazyVec<Month1, T, Month1, S>,
|
||||
LazyVec<Month3, T, Month3, S>,
|
||||
LazyVec<Month6, T, Month6, S>,
|
||||
LazyVec<Year1, T, Year1, S>,
|
||||
LazyVec<Year10, T, Year10, S>,
|
||||
LazyVec<Halving, T, Halving, S>,
|
||||
LazyVec<Epoch, T, Epoch, S>,
|
||||
>,
|
||||
)
|
||||
where
|
||||
@@ -153,7 +153,7 @@ where
|
||||
) -> Self {
|
||||
macro_rules! period {
|
||||
($idx:ident) => {
|
||||
LazyVecFrom1::transformed::<Transform>(
|
||||
LazyVec::transformed::<Transform>(
|
||||
name,
|
||||
version,
|
||||
source.$idx.read_only_boxed_clone(),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use brk_error::Result;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_types::Version;
|
||||
use vecdb::{Database, LazyVecFrom1, ReadableCloneableVec};
|
||||
use vecdb::{Database, LazyVec, ReadableCloneableVec};
|
||||
|
||||
use super::Vecs;
|
||||
use crate::{
|
||||
@@ -18,7 +18,7 @@ impl Vecs {
|
||||
) -> Result<Self> {
|
||||
let weight = TxDerivedDistribution::forced_import(db, "tx_weight", version, indexes)?;
|
||||
|
||||
let tx_index_to_vsize = LazyVecFrom1::transformed::<WeightToVSize>(
|
||||
let tx_index_to_vsize = LazyVec::transformed::<WeightToVSize>(
|
||||
"tx_vsize",
|
||||
version,
|
||||
indexer.vecs.transactions.weight.read_only_boxed_clone(),
|
||||
|
||||
@@ -186,9 +186,7 @@ impl Query {
|
||||
}
|
||||
|
||||
fn is_aggregate_cohort(cohort: &Cohort) -> bool {
|
||||
UTXO_AGGREGATE_NAMES
|
||||
.iter()
|
||||
.any(|name| name.id == &**cohort)
|
||||
UTXO_AGGREGATE_NAMES.iter().any(|name| name.id == &**cohort)
|
||||
}
|
||||
|
||||
fn dates_in_dir(dir: &Path) -> Result<Vec<Date>> {
|
||||
|
||||
@@ -51,7 +51,7 @@ impl<'a> Vecs<'a> {
|
||||
let mut builder = Builder::default();
|
||||
indexed_vecs.for_each(|vec| builder.insert(vec, "indexed"));
|
||||
computed_vecs.for_each(|(db, vec)| builder.insert(vec, db));
|
||||
builder.counts.distinct_series = builder.series_to_index_to_vec.len();
|
||||
builder.counts.distinct = builder.series_to_index_to_vec.len();
|
||||
let Builder {
|
||||
series_to_index_to_vec,
|
||||
index_to_series_to_vec,
|
||||
@@ -205,17 +205,17 @@ impl<'a> Builder<'a> {
|
||||
|
||||
let is_lazy = vec.region_names().is_empty();
|
||||
let by_db = self.counts_by_db.entry(db.to_string()).or_default();
|
||||
self.counts.total_endpoints += 1;
|
||||
by_db.total_endpoints += 1;
|
||||
self.counts.total += 1;
|
||||
by_db.total += 1;
|
||||
if is_lazy {
|
||||
self.counts.lazy_endpoints += 1;
|
||||
by_db.lazy_endpoints += 1;
|
||||
self.counts.lazy += 1;
|
||||
by_db.lazy += 1;
|
||||
} else {
|
||||
self.counts.stored_endpoints += 1;
|
||||
by_db.stored_endpoints += 1;
|
||||
self.counts.stored += 1;
|
||||
by_db.stored += 1;
|
||||
}
|
||||
if self.seen_by_db.entry(db).or_default().insert(name) {
|
||||
by_db.distinct_series += 1;
|
||||
by_db.distinct += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ All vecdb vector types implement `Traversable`:
|
||||
- `BytesVec`, `EagerVec`, `PcoVec` (with `pco` feature)
|
||||
- `ZeroCopyVec` (with `zerocopy` feature)
|
||||
- `LZ4Vec`, `ZstdVec` (with respective features)
|
||||
- `LazyVecFrom1/2/3` for derived vectors
|
||||
- `LazyVec` for single-source derived vectors
|
||||
|
||||
## Feature Flags
|
||||
|
||||
|
||||
@@ -9,9 +9,8 @@ use schemars::JsonSchema;
|
||||
use serde::Serialize;
|
||||
use vecdb::{
|
||||
AggFold, AnyExportableVec, AnyVec, BytesVec, BytesVecValue, CachedVec, CompressionStrategy,
|
||||
DeltaOp, EagerVec, Formattable, LazyAggVec, LazyDeltaVec, LazyVecFrom1, LazyVecFrom2,
|
||||
LazyVecFrom3, RawStrategy, ReadOnlyCompressedVec, ReadOnlyRawVec, StoredVec, TypedVec,
|
||||
VecIndex, VecValue,
|
||||
DeltaOp, EagerVec, Formattable, LazyAggVec, LazyDeltaVec, LazyVec, RawStrategy,
|
||||
ReadOnlyCompressedVec, ReadOnlyRawVec, StoredVec, TypedVec, VecIndex, VecValue,
|
||||
};
|
||||
|
||||
pub trait Traversable {
|
||||
@@ -168,7 +167,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, T, S1I, S1T> Traversable for LazyVecFrom1<I, T, S1I, S1T>
|
||||
impl<I, T, S1I, S1T> Traversable for LazyVec<I, T, S1I, S1T>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: VecValue + Formattable + Serialize + JsonSchema,
|
||||
@@ -184,45 +183,6 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, T, S1I, S1T, S2I, S2T> Traversable for LazyVecFrom2<I, T, S1I, S1T, S2I, S2T>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: VecValue + Formattable + Serialize + JsonSchema,
|
||||
S1I: VecIndex,
|
||||
S1T: VecValue,
|
||||
S2I: VecIndex,
|
||||
S2T: VecValue,
|
||||
{
|
||||
fn iter_any_exportable(&self) -> impl Iterator<Item = &dyn AnyExportableVec> {
|
||||
std::iter::once(self as &dyn AnyExportableVec)
|
||||
}
|
||||
|
||||
fn to_tree_node(&self) -> TreeNode {
|
||||
make_leaf::<I, T, _>(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, T, S1I, S1T, S2I, S2T, S3I, S3T> Traversable
|
||||
for LazyVecFrom3<I, T, S1I, S1T, S2I, S2T, S3I, S3T>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: VecValue + Formattable + Serialize + JsonSchema,
|
||||
S1I: VecIndex,
|
||||
S1T: VecValue,
|
||||
S2I: VecIndex,
|
||||
S2T: VecValue,
|
||||
S3I: VecIndex,
|
||||
S3T: VecValue,
|
||||
{
|
||||
fn iter_any_exportable(&self) -> impl Iterator<Item = &dyn AnyExportableVec> {
|
||||
std::iter::once(self as &dyn AnyExportableVec)
|
||||
}
|
||||
|
||||
fn to_tree_node(&self) -> TreeNode {
|
||||
make_leaf::<I, T, _>(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, O, S1I, S2T, S1T, Strat> Traversable for LazyAggVec<I, O, S1I, S2T, S1T, Strat>
|
||||
where
|
||||
I: VecIndex,
|
||||
|
||||
@@ -3,21 +3,21 @@ use std::collections::BTreeMap;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Series count statistics - distinct series and total series-index combinations
|
||||
/// Series count statistics
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct SeriesCount {
|
||||
/// Number of unique series available (e.g., realized_price, market_cap)
|
||||
#[schemars(example = 3141)]
|
||||
pub distinct_series: usize,
|
||||
pub distinct: usize,
|
||||
/// Total number of series-index combinations across all timeframes
|
||||
#[schemars(example = 21000)]
|
||||
pub total_endpoints: usize,
|
||||
pub total: usize,
|
||||
/// Number of lazy (computed on-the-fly) series-index combinations
|
||||
#[schemars(example = 5000)]
|
||||
pub lazy_endpoints: usize,
|
||||
pub lazy: usize,
|
||||
/// Number of eager (stored on disk) series-index combinations
|
||||
#[schemars(example = 16000)]
|
||||
pub stored_endpoints: usize,
|
||||
pub stored: usize,
|
||||
}
|
||||
|
||||
/// Detailed series count with per-database breakdown
|
||||
|
||||
@@ -936,10 +936,10 @@ mod tests {
|
||||
|
||||
// ========== Case 7: LazyBlockValue ==========
|
||||
// LazyBlockValue (no merge):
|
||||
// - sats: LazyVecFrom1 with wrap="sats"
|
||||
// - sats: LazyVec with wrap="sats"
|
||||
// - rest: LazyDerivedBlockValue with flatten
|
||||
// - bitcoin: LazyVecFrom1 (plain field)
|
||||
// - dollars: Option<LazyVecFrom2> (plain field)
|
||||
// - bitcoin: LazyVec (plain field)
|
||||
// - dollars: optional derived vector (plain field)
|
||||
|
||||
#[test]
|
||||
fn case7_lazy_block_value() {
|
||||
|
||||
@@ -17,6 +17,7 @@ default = []
|
||||
lz4 = ["dep:lz4_flex"]
|
||||
|
||||
[dependencies]
|
||||
arc-swap = "1.9.2"
|
||||
byteorder = { package = "byteorder-lite", version = "0.1.0" }
|
||||
byteview = { workspace = true }
|
||||
crossbeam-skiplist = "0.1.3"
|
||||
|
||||
@@ -27,13 +27,16 @@ impl<'a> BitArrayReader<'a> {
|
||||
self.0
|
||||
}
|
||||
|
||||
/// Gets the i-th bit.
|
||||
/// Gets the i-th bit without checking the byte index.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// `idx` must be less than the number of bits in this array.
|
||||
#[must_use]
|
||||
pub fn get(&self, idx: usize) -> bool {
|
||||
pub unsafe fn get_unchecked(&self, idx: usize) -> bool {
|
||||
let byte_idx = idx / 8;
|
||||
|
||||
#[expect(clippy::expect_used, reason = "we trust the caller")]
|
||||
let byte = self.0.get(byte_idx).expect("should be in bounds");
|
||||
debug_assert!(byte_idx < self.0.len());
|
||||
let byte = unsafe { self.0.get_unchecked(byte_idx) };
|
||||
|
||||
let bit_idx = idx % 8;
|
||||
get_bit(*byte, bit_idx)
|
||||
|
||||
@@ -77,14 +77,21 @@ impl<'a> StandardBloomFilterReader<'a> {
|
||||
)]
|
||||
let offset = reader.position() as usize;
|
||||
|
||||
#[expect(
|
||||
clippy::expect_used,
|
||||
reason = "offset is expected to be with slice bounds"
|
||||
)]
|
||||
let bytes = slice
|
||||
.get(offset..)
|
||||
.ok_or(crate::Error::InvalidHeader("BloomFilter"))?;
|
||||
let bit_len = bytes
|
||||
.len()
|
||||
.checked_mul(8)
|
||||
.ok_or(crate::Error::InvalidHeader("BloomFilter"))?;
|
||||
if m == 0 || m != bit_len {
|
||||
return Err(crate::Error::InvalidHeader("BloomFilter"));
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
k,
|
||||
m,
|
||||
inner: BitArrayReader::new(slice.get(offset..).expect("should be in bounds")),
|
||||
inner: BitArrayReader::new(bytes),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -130,7 +137,11 @@ impl<'a> StandardBloomFilterReader<'a> {
|
||||
|
||||
/// Returns `true` if the bit at `idx` is `1`.
|
||||
fn has_bit(&self, idx: usize) -> bool {
|
||||
self.inner.get(idx)
|
||||
debug_assert!(idx < self.m);
|
||||
|
||||
// SAFETY: construction validates that `m` matches the bit array, and
|
||||
// callers derive `idx` modulo `m`.
|
||||
unsafe { self.inner.get_unchecked(idx) }
|
||||
}
|
||||
|
||||
/// Gets the hash of a key.
|
||||
@@ -169,6 +180,18 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn filter_bloom_standard_rejects_truncated_bits() {
|
||||
let filter = Builder::with_fp_rate(10, 0.0001);
|
||||
let mut filter_bytes = filter.build();
|
||||
filter_bytes.pop();
|
||||
|
||||
assert!(matches!(
|
||||
StandardBloomFilterReader::new(&filter_bytes),
|
||||
Err(crate::Error::InvalidHeader("BloomFilter"))
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn filter_bloom_standard_basic() -> crate::Result<()> {
|
||||
let mut filter = Builder::with_fp_rate(10, 0.0001);
|
||||
|
||||
@@ -8,8 +8,9 @@ use crate::{
|
||||
config::Config,
|
||||
stop_signal::StopSignal,
|
||||
table::next_table_id,
|
||||
version::{SuperVersions, Version, persist_version},
|
||||
version::{SuperVersion, SuperVersions, Version, persist_version},
|
||||
};
|
||||
use arc_swap::ArcSwap;
|
||||
use std::sync::{Arc, Mutex, RwLock, atomic::AtomicU64};
|
||||
|
||||
/// Unique tree ID
|
||||
@@ -45,6 +46,8 @@ pub struct TreeInner {
|
||||
|
||||
pub(crate) version_history: Arc<RwLock<SuperVersions>>,
|
||||
|
||||
pub(crate) latest_version: Arc<ArcSwap<SuperVersion>>,
|
||||
|
||||
pub(crate) compaction_state: Arc<Mutex<CompactionState>>,
|
||||
|
||||
/// Tree configuration
|
||||
@@ -68,13 +71,16 @@ impl TreeInner {
|
||||
pub(crate) fn create_new(config: Config) -> crate::Result<Self> {
|
||||
let version = Version::new(0);
|
||||
persist_version(&config.path, &version)?;
|
||||
let version_history = SuperVersions::new(version);
|
||||
let latest_version = version_history.latest_version_reader();
|
||||
|
||||
Ok(Self {
|
||||
id: get_next_tree_id(),
|
||||
memtable_id_counter: SequenceNumberCounter::new(1),
|
||||
table_id_counter: SequenceNumberCounter::default(),
|
||||
config: Arc::new(config),
|
||||
version_history: Arc::new(RwLock::new(SuperVersions::new(version))),
|
||||
version_history: Arc::new(RwLock::new(version_history)),
|
||||
latest_version,
|
||||
stop_signal: StopSignal::default(),
|
||||
major_compaction_lock: RwLock::default(),
|
||||
flush_lock: Mutex::default(),
|
||||
|
||||
@@ -873,12 +873,7 @@ impl Tree {
|
||||
#[doc(hidden)]
|
||||
pub fn get_exclusive<K: AsRef<[u8]>>(&self, key: K) -> crate::Result<Option<UserValue>> {
|
||||
let key = key.as_ref();
|
||||
#[expect(clippy::expect_used, reason = "lock is expected to not be poisoned")]
|
||||
let super_version = self
|
||||
.version_history
|
||||
.read()
|
||||
.expect("lock is poisoned")
|
||||
.latest_version_arc();
|
||||
let super_version = self.latest_version.load();
|
||||
|
||||
Self::get_value_from_tables(&super_version.version, key, SeqNo::MAX)
|
||||
}
|
||||
@@ -970,17 +965,19 @@ impl Tree {
|
||||
}
|
||||
|
||||
let version = Self::recover_levels(&config.path, tree_id, &config)?;
|
||||
|
||||
let highest_table_id = version
|
||||
.iter_tables()
|
||||
.map(Table::id)
|
||||
.max()
|
||||
.unwrap_or_default();
|
||||
let version_history = SuperVersions::new(version);
|
||||
let latest_version = version_history.latest_version_reader();
|
||||
let inner = TreeInner {
|
||||
id: tree_id,
|
||||
memtable_id_counter: SequenceNumberCounter::new(1),
|
||||
table_id_counter: SequenceNumberCounter::new(u64::from(highest_table_id) + 1),
|
||||
version_history: Arc::new(RwLock::new(SuperVersions::new(version))),
|
||||
version_history: Arc::new(RwLock::new(version_history)),
|
||||
latest_version,
|
||||
stop_signal: StopSignal::default(),
|
||||
config: Arc::new(config),
|
||||
major_compaction_lock: RwLock::default(),
|
||||
|
||||
@@ -8,6 +8,7 @@ use crate::{
|
||||
tree::sealed::SealedMemtables,
|
||||
version::{Version, persist_version},
|
||||
};
|
||||
use arc_swap::ArcSwap;
|
||||
use std::{collections::VecDeque, path::Path, sync::Arc};
|
||||
|
||||
/// A super version is a point-in-time snapshot of memtables and a [`Version`] (list of disk files)
|
||||
@@ -26,25 +27,30 @@ pub struct SuperVersion {
|
||||
pub(crate) seqno: SeqNo,
|
||||
}
|
||||
|
||||
pub struct SuperVersions(VecDeque<Arc<SuperVersion>>);
|
||||
pub struct SuperVersions {
|
||||
versions: VecDeque<Arc<SuperVersion>>,
|
||||
latest: Arc<ArcSwap<SuperVersion>>,
|
||||
}
|
||||
|
||||
impl SuperVersions {
|
||||
pub fn new(version: Version) -> Self {
|
||||
Self(
|
||||
vec![Arc::new(SuperVersion {
|
||||
active_memtable: Arc::new(Memtable::new(0)),
|
||||
sealed_memtables: Arc::default(),
|
||||
version,
|
||||
seqno: 0,
|
||||
})]
|
||||
.into(),
|
||||
)
|
||||
let version = Arc::new(SuperVersion {
|
||||
active_memtable: Arc::new(Memtable::new(0)),
|
||||
sealed_memtables: Arc::default(),
|
||||
version,
|
||||
seqno: 0,
|
||||
});
|
||||
|
||||
Self {
|
||||
versions: vec![version.clone()].into(),
|
||||
latest: Arc::new(ArcSwap::from(version)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn memtable_size_sum(&self) -> u64 {
|
||||
let mut set = crate::HashMap::default();
|
||||
|
||||
for super_version in &self.0 {
|
||||
for super_version in &self.versions {
|
||||
set.entry(super_version.active_memtable.id)
|
||||
.and_modify(|bytes| *bytes += super_version.active_memtable.size())
|
||||
.or_insert_with(|| super_version.active_memtable.size());
|
||||
@@ -60,7 +66,7 @@ impl SuperVersions {
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.0.len()
|
||||
self.versions.len()
|
||||
}
|
||||
|
||||
pub fn free_list_len(&self) -> usize {
|
||||
@@ -78,9 +84,9 @@ impl SuperVersions {
|
||||
|
||||
log::trace!("Running manifest GC with watermark={gc_watermark}");
|
||||
|
||||
if let Some(hi_idx) = self.0.iter().rposition(|x| x.seqno < gc_watermark) {
|
||||
if let Some(hi_idx) = self.versions.iter().rposition(|x| x.seqno < gc_watermark) {
|
||||
for _ in 0..hi_idx {
|
||||
let Some(head) = self.0.front() else {
|
||||
let Some(head) = self.versions.front() else {
|
||||
break;
|
||||
};
|
||||
|
||||
@@ -95,11 +101,14 @@ impl SuperVersions {
|
||||
crate::file::retry_transient_io(|| std::fs::remove_file(&path))?;
|
||||
}
|
||||
|
||||
self.0.pop_front();
|
||||
self.versions.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
log::trace!("Manifest GC done, version length now {}", self.0.len());
|
||||
log::trace!(
|
||||
"Manifest GC done, version length now {}",
|
||||
self.versions.len()
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -146,43 +155,43 @@ impl SuperVersions {
|
||||
}
|
||||
|
||||
pub fn append_version(&mut self, version: SuperVersion) {
|
||||
self.0.push_back(Arc::new(version));
|
||||
let version = Arc::new(version);
|
||||
self.versions.push_back(version.clone());
|
||||
self.latest.store(version);
|
||||
}
|
||||
|
||||
pub fn replace_latest_version(&mut self, version: SuperVersion) {
|
||||
if self.0.pop_back().is_some() {
|
||||
self.0.push_back(Arc::new(version));
|
||||
if let Some(latest) = self.versions.back_mut() {
|
||||
let version = Arc::new(version);
|
||||
*latest = version.clone();
|
||||
self.latest.store(version);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn latest_version(&self) -> SuperVersion {
|
||||
#[expect(clippy::expect_used, reason = "SuperVersion is expected to exist")]
|
||||
self.0
|
||||
self.versions
|
||||
.back()
|
||||
.map(|version| version.as_ref().clone())
|
||||
.expect("should always have a SuperVersion")
|
||||
}
|
||||
|
||||
pub(crate) fn latest_version_arc(&self) -> Arc<SuperVersion> {
|
||||
#[expect(clippy::expect_used, reason = "SuperVersion is expected to exist")]
|
||||
self.0
|
||||
.back()
|
||||
.cloned()
|
||||
.expect("should always have a SuperVersion")
|
||||
pub(crate) fn latest_version_reader(&self) -> Arc<ArcSwap<SuperVersion>> {
|
||||
self.latest.clone()
|
||||
}
|
||||
|
||||
pub(crate) fn get_version_arc_for_snapshot(&self, seqno: SeqNo) -> Arc<SuperVersion> {
|
||||
if seqno == 0 {
|
||||
#[expect(clippy::expect_used, reason = "SuperVersion is expected to exist")]
|
||||
return self
|
||||
.0
|
||||
.versions
|
||||
.front()
|
||||
.cloned()
|
||||
.expect("should always find a SuperVersion");
|
||||
}
|
||||
|
||||
let version = self
|
||||
.0
|
||||
.versions
|
||||
.iter()
|
||||
.rev()
|
||||
.find(|version| version.seqno < seqno)
|
||||
@@ -192,7 +201,7 @@ impl SuperVersions {
|
||||
log::error!("Failed to find a SuperVersion for snapshot with seqno={seqno}");
|
||||
log::error!("SuperVersions:");
|
||||
|
||||
for version in self.0.iter().rev() {
|
||||
for version in self.versions.iter().rev() {
|
||||
log::error!("-> {}, seqno={}", version.version.id(), version.seqno);
|
||||
}
|
||||
}
|
||||
@@ -207,7 +216,17 @@ impl SuperVersions {
|
||||
|
||||
#[cfg(test)]
|
||||
fn from_versions(versions: VecDeque<SuperVersion>) -> Self {
|
||||
Self(versions.into_iter().map(Arc::new).collect())
|
||||
let versions: VecDeque<_> = versions.into_iter().map(Arc::new).collect();
|
||||
#[expect(clippy::expect_used, reason = "test histories are non-empty")]
|
||||
let latest = versions
|
||||
.back()
|
||||
.cloned()
|
||||
.expect("history should not be empty");
|
||||
|
||||
Self {
|
||||
versions,
|
||||
latest: Arc::new(ArcSwap::from(latest)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -216,6 +235,26 @@ mod tests {
|
||||
use super::*;
|
||||
use test_log::test;
|
||||
|
||||
#[test]
|
||||
fn latest_reader_tracks_publications() {
|
||||
let mut history = SuperVersions::new(Version::new(0));
|
||||
let reader = history.latest_version_reader();
|
||||
let original = reader.load_full();
|
||||
|
||||
let mut appended = history.latest_version();
|
||||
appended.version = Version::new(1);
|
||||
history.append_version(appended);
|
||||
|
||||
assert_eq!(original.version.id(), 0);
|
||||
assert_eq!(reader.load().version.id(), 1);
|
||||
|
||||
let mut replacement = history.latest_version();
|
||||
replacement.version = Version::new(2);
|
||||
history.replace_latest_version(replacement);
|
||||
|
||||
assert_eq!(reader.load().version.id(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn super_version_gc_above_watermark() -> crate::Result<()> {
|
||||
let mut history = SuperVersions::from_versions(
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
use lsm_tree::{AbstractTree, Config, SequenceNumberCounter, get_tmp_folder};
|
||||
use test_log::test;
|
||||
|
||||
#[test]
|
||||
fn exclusive_point_read_tracks_latest_version() -> lsm_tree::Result<()> {
|
||||
let folder = get_tmp_folder();
|
||||
let seqno = SequenceNumberCounter::default();
|
||||
let tree = Config::new(
|
||||
folder.path(),
|
||||
seqno.clone(),
|
||||
SequenceNumberCounter::default(),
|
||||
)
|
||||
.open()?;
|
||||
|
||||
tree.insert("a", "first", seqno.next());
|
||||
tree.flush_active_memtable(0)?;
|
||||
assert_eq!(
|
||||
tree.get_exclusive("a")?.as_deref(),
|
||||
Some(b"first".as_slice())
|
||||
);
|
||||
|
||||
tree.insert("b", "second", seqno.next());
|
||||
tree.flush_active_memtable(0)?;
|
||||
assert_eq!(
|
||||
tree.get_exclusive("a")?.as_deref(),
|
||||
Some(b"first".as_slice())
|
||||
);
|
||||
assert_eq!(
|
||||
tree.get_exclusive("b")?.as_deref(),
|
||||
Some(b"second".as_slice())
|
||||
);
|
||||
|
||||
tree.major_compact(u64::MAX, 1_000)?;
|
||||
assert_eq!(
|
||||
tree.get_exclusive("a")?.as_deref(),
|
||||
Some(b"first".as_slice())
|
||||
);
|
||||
assert_eq!(
|
||||
tree.get_exclusive("b")?.as_deref(),
|
||||
Some(b"second".as_slice())
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -8,7 +8,7 @@ High-performance mutable persistent vectors built on [`rawdb`](../rawdb/README.m
|
||||
- **Multiple storage formats**:
|
||||
- **Raw**: `BytesVec`, `ZeroCopyVec` (uncompressed)
|
||||
- **Compressed**: `PcoVec`, `LZ4Vec`, `ZstdVec`
|
||||
- **Computed vectors**: `EagerVec` (stored computations), `LazyVecFrom1/2/3` (on-the-fly computation)
|
||||
- **Computed vectors**: `EagerVec` (stored computations), `LazyVec` (single-source on-the-fly computation)
|
||||
- **Rollback support**: Time-travel via stamped change deltas without full snapshots
|
||||
- **Sparse deletions**: Delete elements leaving holes, no reindexing required
|
||||
- **Thread-safe**: Concurrent reads with exclusive writes
|
||||
@@ -144,14 +144,14 @@ let mut derived: EagerVec<BytesVec<usize, f64>> =
|
||||
// derived.compute_sma(&source, 20)?;
|
||||
```
|
||||
|
||||
**`LazyVecFrom1/2/3<...>`** - Lazily computed vectors from 1-3 source vectors
|
||||
**`LazyVec<...>`** - Lazily computed vector from one source vector
|
||||
|
||||
Values computed on-the-fly during iteration, nothing stored on disk. Use for temporary views or simple transformations.
|
||||
|
||||
```rust,ignore
|
||||
use vecdb::LazyVecFrom1;
|
||||
use vecdb::LazyVec;
|
||||
|
||||
let lazy = LazyVecFrom1::init(
|
||||
let lazy = LazyVec::init(
|
||||
"computed",
|
||||
Version::TWO,
|
||||
Box::new(source.clone()), // ScannableBoxedVec
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ pub use minus::Minus;
|
||||
pub use plus::Plus;
|
||||
pub use times::Times;
|
||||
|
||||
/// Trait for binary transforms applied lazily during iteration.
|
||||
/// Trait for binary transforms used by vector computations.
|
||||
/// Zero-sized types implementing this get monomorphized (zero runtime cost).
|
||||
pub trait BinaryTransform<In1, In2, Out = In1> {
|
||||
fn apply(lhs: In1, rhs: In2) -> Out;
|
||||
@@ -1,7 +1,9 @@
|
||||
//! Numeric traits (overflow-safe arithmetic) shared across the crate.
|
||||
|
||||
mod binary_transform;
|
||||
mod checked_sub;
|
||||
mod saturating_add;
|
||||
|
||||
pub use binary_transform::*;
|
||||
pub use checked_sub::*;
|
||||
pub use saturating_add::*;
|
||||
|
||||
@@ -176,7 +176,7 @@ impl<V: TypedVec + ReadableVec<V::I, V::T>> CachedVec<V> {
|
||||
}
|
||||
|
||||
impl<V: StoredVec> CachedVec<V> {
|
||||
/// Boxes a read-only clone for use with type-erased APIs (e.g. LazyVecFrom1).
|
||||
/// Boxes a read-only clone for use with type-erased APIs (e.g. LazyVec).
|
||||
#[inline]
|
||||
pub fn read_only_boxed_clone(&self) -> crate::ReadableBoxedVec<V::I, V::T> {
|
||||
Box::new(self.read_only_clone())
|
||||
|
||||
@@ -20,7 +20,7 @@ where
|
||||
) -> Result<()>
|
||||
where
|
||||
O: ReadableVec<V::I, V::T>,
|
||||
F: Fn(&[Vec<V::T>], usize) -> V::T,
|
||||
F: Fn(&mut V::T, V::T),
|
||||
{
|
||||
if others.is_empty() {
|
||||
return Err(Error::InvalidArgument(
|
||||
@@ -40,13 +40,18 @@ where
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let batches: Vec<Vec<V::T>> = others
|
||||
.iter()
|
||||
.map(|v| v.collect_range_at(skip, end))
|
||||
.collect();
|
||||
let mut batches = others.iter().map(|v| v.collect_range_at(skip, end));
|
||||
let mut aggregated = batches.next().unwrap();
|
||||
|
||||
for j in 0..(end - skip) {
|
||||
this.push(aggregate(&batches, j));
|
||||
for batch in batches {
|
||||
debug_assert_eq!(aggregated.len(), batch.len());
|
||||
for (value, other) in aggregated.iter_mut().zip(batch) {
|
||||
aggregate(value, other);
|
||||
}
|
||||
}
|
||||
|
||||
for value in aggregated {
|
||||
this.push(value);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -64,12 +69,8 @@ where
|
||||
O: ReadableVec<V::I, V::T>,
|
||||
V::T: Add<V::T, Output = V::T>,
|
||||
{
|
||||
self.compute_aggregate_of_others(max_from, others, exit, |batches, j| {
|
||||
batches
|
||||
.iter()
|
||||
.map(|b| b[j].clone())
|
||||
.reduce(|sum, v| sum + v)
|
||||
.unwrap()
|
||||
self.compute_aggregate_of_others(max_from, others, exit, |sum, value| {
|
||||
*sum = sum.clone() + value;
|
||||
})
|
||||
}
|
||||
|
||||
@@ -83,8 +84,10 @@ where
|
||||
O: ReadableVec<V::I, V::T>,
|
||||
V::T: Add<V::T, Output = V::T> + Ord,
|
||||
{
|
||||
self.compute_aggregate_of_others(max_from, others, exit, |batches, j| {
|
||||
batches.iter().map(|b| &b[j]).min().unwrap().clone()
|
||||
self.compute_aggregate_of_others(max_from, others, exit, |min, value| {
|
||||
if value.lt(min) {
|
||||
*min = value;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -98,8 +101,10 @@ where
|
||||
O: ReadableVec<V::I, V::T>,
|
||||
V::T: Add<V::T, Output = V::T> + Ord,
|
||||
{
|
||||
self.compute_aggregate_of_others(max_from, others, exit, |batches, j| {
|
||||
batches.iter().map(|b| &b[j]).max().unwrap().clone()
|
||||
self.compute_aggregate_of_others(max_from, others, exit, |max, value| {
|
||||
if value.gt(max) {
|
||||
*max = value;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
use crate::{AnyVec, VecIndex, VecValue, Version, short_type_name};
|
||||
|
||||
use super::LazyVecFrom2;
|
||||
|
||||
impl<I, T, S1I, S1T, S2I, S2T> AnyVec for LazyVecFrom2<I, T, S1I, S1T, S2I, S2T>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: VecValue,
|
||||
S1I: VecIndex,
|
||||
S1T: VecValue,
|
||||
S2I: VecIndex,
|
||||
S2T: VecValue,
|
||||
{
|
||||
fn version(&self) -> Version {
|
||||
self.base_version + self.source1.version() + self.source2.version()
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
fn index_type_to_string(&self) -> &'static str {
|
||||
I::to_string()
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
let len1 = if self.s1_counts {
|
||||
self.source1.len()
|
||||
} else {
|
||||
usize::MAX
|
||||
};
|
||||
let len2 = if self.s2_counts {
|
||||
self.source2.len()
|
||||
} else {
|
||||
usize::MAX
|
||||
};
|
||||
len1.min(len2)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn value_type_to_size_of(&self) -> usize {
|
||||
size_of::<T>()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn value_type_to_string(&self) -> &'static str {
|
||||
short_type_name::<T>()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn region_names(&self) -> Vec<String> {
|
||||
Vec::new()
|
||||
}
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
mod any_vec;
|
||||
mod readable;
|
||||
mod transform;
|
||||
mod typed;
|
||||
|
||||
pub use transform::*;
|
||||
|
||||
use crate::{ReadableBoxedVec, VecIndex, VecValue, Version};
|
||||
|
||||
pub type ComputeFrom2<I, T, S1T, S2T> = fn(I, S1T, S2T) -> T;
|
||||
|
||||
/// Lazily computed vector deriving values from two source vectors.
|
||||
///
|
||||
/// Values are computed on-the-fly during iteration using a provided function.
|
||||
/// Nothing is stored on disk - all values are recomputed each time they're accessed.
|
||||
#[derive(Clone)]
|
||||
pub struct LazyVecFrom2<I, T, S1I, S1T, S2I, S2T>
|
||||
where
|
||||
S1I: VecIndex,
|
||||
S1T: VecValue,
|
||||
S2I: VecIndex,
|
||||
S2T: VecValue,
|
||||
{
|
||||
pub(super) name: Arc<str>,
|
||||
pub(super) base_version: Version,
|
||||
pub(super) source1: ReadableBoxedVec<S1I, S1T>,
|
||||
pub(super) source2: ReadableBoxedVec<S2I, S2T>,
|
||||
pub(super) compute: ComputeFrom2<I, T, S1T, S2T>,
|
||||
pub(super) s1_counts: bool,
|
||||
pub(super) s2_counts: bool,
|
||||
}
|
||||
|
||||
impl<I, T, S1I, S1T, S2I, S2T> LazyVecFrom2<I, T, S1I, S1T, S2I, S2T>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: VecValue,
|
||||
S1I: VecIndex,
|
||||
S1T: VecValue,
|
||||
S2I: VecIndex,
|
||||
S2T: VecValue,
|
||||
{
|
||||
pub fn init(
|
||||
name: &str,
|
||||
version: Version,
|
||||
source1: ReadableBoxedVec<S1I, S1T>,
|
||||
source2: ReadableBoxedVec<S2I, S2T>,
|
||||
compute: ComputeFrom2<I, T, S1T, S2T>,
|
||||
) -> Self {
|
||||
let target = I::to_string();
|
||||
let s1 = source1.index_type_to_string();
|
||||
let s2 = source2.index_type_to_string();
|
||||
|
||||
assert!(
|
||||
s1 == target || s2 == target,
|
||||
"LazyVecFrom2: at least one source must have index type {}, got {} and {}",
|
||||
target,
|
||||
s1,
|
||||
s2
|
||||
);
|
||||
|
||||
let s1_counts = s1 == target;
|
||||
let s2_counts = s2 == target;
|
||||
|
||||
Self {
|
||||
name: Arc::from(name),
|
||||
base_version: version,
|
||||
source1,
|
||||
source2,
|
||||
compute,
|
||||
s1_counts,
|
||||
s2_counts,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, T, S1T, S2T> LazyVecFrom2<I, T, I, S1T, I, S2T>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: VecValue,
|
||||
S1T: VecValue,
|
||||
S2T: VecValue,
|
||||
{
|
||||
/// Create a lazy vec with a generic binary transform.
|
||||
/// Usage: `LazyVecFrom2::transformed::<Divide>(name, v, source1, source2)`
|
||||
pub fn transformed<F: BinaryTransform<S1T, S2T, T>>(
|
||||
name: &str,
|
||||
version: Version,
|
||||
source1: ReadableBoxedVec<I, S1T>,
|
||||
source2: ReadableBoxedVec<I, S2T>,
|
||||
) -> Self {
|
||||
Self::init(name, version, source1, source2, |_, a, b| F::apply(a, b))
|
||||
}
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
use crate::{AnyVec, ReadableVec, VecIndex, VecValue};
|
||||
|
||||
use super::LazyVecFrom2;
|
||||
|
||||
impl<I, T, S1I, S1T, S2I, S2T> ReadableVec<I, T> for LazyVecFrom2<I, T, S1I, S1T, S2I, S2T>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: VecValue,
|
||||
S1I: VecIndex,
|
||||
S1T: VecValue,
|
||||
S2I: VecIndex,
|
||||
S2T: VecValue,
|
||||
{
|
||||
#[inline]
|
||||
fn read_into_at(&self, from: usize, to: usize, buf: &mut Vec<T>) {
|
||||
let to = to.min(self.len());
|
||||
buf.reserve(to.saturating_sub(from));
|
||||
self.for_each_range_dyn_at(from, to, &mut |v| buf.push(v));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn for_each_range_dyn_at(&self, from: usize, to: usize, f: &mut dyn FnMut(T)) {
|
||||
let compute = self.compute;
|
||||
let to = to.min(self.len());
|
||||
let buf1 = self.source1.collect_range_dyn(from, to);
|
||||
let buf2 = self.source2.collect_range_dyn(from, to);
|
||||
buf1.into_iter()
|
||||
.zip(buf2)
|
||||
.enumerate()
|
||||
.for_each(|(local, (v1, v2))| {
|
||||
f(compute(I::from(from + local), v1, v2));
|
||||
});
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn fold_range_at<B, F: FnMut(B, T) -> B>(&self, from: usize, to: usize, init: B, mut f: F) -> B
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
self.try_fold_range_at(from, to, init, |acc, v| {
|
||||
Ok::<_, std::convert::Infallible>(f(acc, v))
|
||||
})
|
||||
.unwrap_or_else(|e: std::convert::Infallible| match e {})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_fold_range_at<B, E, F: FnMut(B, T) -> std::result::Result<B, E>>(
|
||||
&self,
|
||||
from: usize,
|
||||
to: usize,
|
||||
init: B,
|
||||
mut f: F,
|
||||
) -> std::result::Result<B, E>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let to = to.min(self.len());
|
||||
if from >= to {
|
||||
return Ok(init);
|
||||
}
|
||||
let compute = self.compute;
|
||||
let buf1 = self.source1.collect_range_dyn(from, to);
|
||||
let buf2 = self.source2.collect_range_dyn(from, to);
|
||||
buf1.into_iter()
|
||||
.zip(buf2)
|
||||
.enumerate()
|
||||
.try_fold(init, |acc, (local, (v1, v2))| {
|
||||
f(acc, compute(I::from(from + local), v1, v2))
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn collect_one_at(&self, index: usize) -> Option<T> {
|
||||
if index >= self.len() {
|
||||
return None;
|
||||
}
|
||||
let v1 = self.source1.collect_one_at(index)?;
|
||||
let v2 = self.source2.collect_one_at(index)?;
|
||||
Some((self.compute)(I::from(index), v1, v2))
|
||||
}
|
||||
|
||||
fn read_sorted_into_at(&self, indices: &[usize], out: &mut Vec<T>) {
|
||||
let compute = self.compute;
|
||||
let vals1 = self.source1.read_sorted_at(indices);
|
||||
let vals2 = self.source2.read_sorted_at(indices);
|
||||
out.reserve(vals1.len().min(vals2.len()));
|
||||
indices
|
||||
.iter()
|
||||
.zip(vals1.into_iter().zip(vals2))
|
||||
.for_each(|(&i, (v1, v2))| out.push(compute(I::from(i), v1, v2)));
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
use crate::{TypedVec, VecIndex, VecValue};
|
||||
|
||||
use super::LazyVecFrom2;
|
||||
|
||||
impl<I, T, S1I, S1T, S2I, S2T> TypedVec for LazyVecFrom2<I, T, S1I, S1T, S2I, S2T>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: VecValue,
|
||||
S1I: VecIndex,
|
||||
S1T: VecValue,
|
||||
S2I: VecIndex,
|
||||
S2T: VecValue,
|
||||
{
|
||||
type I = I;
|
||||
type T = T;
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
use crate::{AnyVec, VecIndex, VecValue, Version, short_type_name};
|
||||
|
||||
use super::LazyVecFrom3;
|
||||
|
||||
impl<I, T, S1I, S1T, S2I, S2T, S3I, S3T> AnyVec for LazyVecFrom3<I, T, S1I, S1T, S2I, S2T, S3I, S3T>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: VecValue,
|
||||
S1I: VecIndex,
|
||||
S1T: VecValue,
|
||||
S2I: VecIndex,
|
||||
S2T: VecValue,
|
||||
S3I: VecIndex,
|
||||
S3T: VecValue,
|
||||
{
|
||||
fn version(&self) -> Version {
|
||||
self.base_version + self.source1.version() + self.source2.version() + self.source3.version()
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
fn index_type_to_string(&self) -> &'static str {
|
||||
I::to_string()
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
let len1 = if self.s1_counts {
|
||||
self.source1.len()
|
||||
} else {
|
||||
usize::MAX
|
||||
};
|
||||
let len2 = if self.s2_counts {
|
||||
self.source2.len()
|
||||
} else {
|
||||
usize::MAX
|
||||
};
|
||||
let len3 = if self.s3_counts {
|
||||
self.source3.len()
|
||||
} else {
|
||||
usize::MAX
|
||||
};
|
||||
len1.min(len2).min(len3)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn value_type_to_size_of(&self) -> usize {
|
||||
size_of::<T>()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn value_type_to_string(&self) -> &'static str {
|
||||
short_type_name::<T>()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn region_names(&self) -> Vec<String> {
|
||||
Vec::new()
|
||||
}
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
mod any_vec;
|
||||
mod readable;
|
||||
mod typed;
|
||||
|
||||
use crate::{ReadableBoxedVec, VecIndex, VecValue, Version};
|
||||
|
||||
pub type ComputeFrom3<I, T, S1T, S2T, S3T> = fn(I, S1T, S2T, S3T) -> T;
|
||||
|
||||
/// Lazily computed vector deriving values from three source vectors.
|
||||
///
|
||||
/// Values are computed on-the-fly during iteration using a provided function.
|
||||
/// Nothing is stored on disk - all values are recomputed each time they're accessed.
|
||||
#[derive(Clone)]
|
||||
pub struct LazyVecFrom3<I, T, S1I, S1T, S2I, S2T, S3I, S3T>
|
||||
where
|
||||
S1I: VecIndex,
|
||||
S1T: VecValue,
|
||||
S2I: VecIndex,
|
||||
S2T: VecValue,
|
||||
S3I: VecIndex,
|
||||
S3T: VecValue,
|
||||
{
|
||||
pub(super) name: Arc<str>,
|
||||
pub(super) base_version: Version,
|
||||
pub(super) source1: ReadableBoxedVec<S1I, S1T>,
|
||||
pub(super) source2: ReadableBoxedVec<S2I, S2T>,
|
||||
pub(super) source3: ReadableBoxedVec<S3I, S3T>,
|
||||
pub(super) compute: ComputeFrom3<I, T, S1T, S2T, S3T>,
|
||||
pub(super) s1_counts: bool,
|
||||
pub(super) s2_counts: bool,
|
||||
pub(super) s3_counts: bool,
|
||||
}
|
||||
|
||||
impl<I, T, S1I, S1T, S2I, S2T, S3I, S3T> LazyVecFrom3<I, T, S1I, S1T, S2I, S2T, S3I, S3T>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: VecValue,
|
||||
S1I: VecIndex,
|
||||
S1T: VecValue,
|
||||
S2I: VecIndex,
|
||||
S2T: VecValue,
|
||||
S3I: VecIndex,
|
||||
S3T: VecValue,
|
||||
{
|
||||
pub fn init(
|
||||
name: &str,
|
||||
version: Version,
|
||||
source1: ReadableBoxedVec<S1I, S1T>,
|
||||
source2: ReadableBoxedVec<S2I, S2T>,
|
||||
source3: ReadableBoxedVec<S3I, S3T>,
|
||||
compute: ComputeFrom3<I, T, S1T, S2T, S3T>,
|
||||
) -> Self {
|
||||
let target = I::to_string();
|
||||
let s1 = source1.index_type_to_string();
|
||||
let s2 = source2.index_type_to_string();
|
||||
let s3 = source3.index_type_to_string();
|
||||
|
||||
assert!(
|
||||
s1 == target || s2 == target || s3 == target,
|
||||
"LazyVecFrom3: at least one source must have index type {}, got {}, {}, and {}",
|
||||
target,
|
||||
s1,
|
||||
s2,
|
||||
s3
|
||||
);
|
||||
|
||||
let s1_counts = s1 == target;
|
||||
let s2_counts = s2 == target;
|
||||
let s3_counts = s3 == target;
|
||||
|
||||
Self {
|
||||
name: Arc::from(name),
|
||||
base_version: version,
|
||||
source1,
|
||||
source2,
|
||||
source3,
|
||||
compute,
|
||||
s1_counts,
|
||||
s2_counts,
|
||||
s3_counts,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
use crate::{AnyVec, ReadableVec, VecIndex, VecValue};
|
||||
|
||||
use super::LazyVecFrom3;
|
||||
|
||||
impl<I, T, S1I, S1T, S2I, S2T, S3I, S3T> ReadableVec<I, T>
|
||||
for LazyVecFrom3<I, T, S1I, S1T, S2I, S2T, S3I, S3T>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: VecValue,
|
||||
S1I: VecIndex,
|
||||
S1T: VecValue,
|
||||
S2I: VecIndex,
|
||||
S2T: VecValue,
|
||||
S3I: VecIndex,
|
||||
S3T: VecValue,
|
||||
{
|
||||
#[inline]
|
||||
fn read_into_at(&self, from: usize, to: usize, buf: &mut Vec<T>) {
|
||||
let to = to.min(self.len());
|
||||
buf.reserve(to.saturating_sub(from));
|
||||
self.for_each_range_dyn_at(from, to, &mut |v| buf.push(v));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn for_each_range_dyn_at(&self, from: usize, to: usize, f: &mut dyn FnMut(T)) {
|
||||
let compute = self.compute;
|
||||
let to = to.min(self.len());
|
||||
let buf1 = self.source1.collect_range_dyn(from, to);
|
||||
let buf2 = self.source2.collect_range_dyn(from, to);
|
||||
let buf3 = self.source3.collect_range_dyn(from, to);
|
||||
buf1.into_iter()
|
||||
.zip(buf2)
|
||||
.zip(buf3)
|
||||
.enumerate()
|
||||
.for_each(|(local, ((v1, v2), v3))| {
|
||||
f(compute(I::from(from + local), v1, v2, v3));
|
||||
});
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn fold_range_at<B, F: FnMut(B, T) -> B>(&self, from: usize, to: usize, init: B, mut f: F) -> B
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
self.try_fold_range_at(from, to, init, |acc, v| {
|
||||
Ok::<_, std::convert::Infallible>(f(acc, v))
|
||||
})
|
||||
.unwrap_or_else(|e: std::convert::Infallible| match e {})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_fold_range_at<B, E, F: FnMut(B, T) -> std::result::Result<B, E>>(
|
||||
&self,
|
||||
from: usize,
|
||||
to: usize,
|
||||
init: B,
|
||||
mut f: F,
|
||||
) -> std::result::Result<B, E>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let to = to.min(self.len());
|
||||
if from >= to {
|
||||
return Ok(init);
|
||||
}
|
||||
let compute = self.compute;
|
||||
let buf1 = self.source1.collect_range_dyn(from, to);
|
||||
let buf2 = self.source2.collect_range_dyn(from, to);
|
||||
let buf3 = self.source3.collect_range_dyn(from, to);
|
||||
buf1.into_iter()
|
||||
.zip(buf2)
|
||||
.zip(buf3)
|
||||
.enumerate()
|
||||
.try_fold(init, |acc, (local, ((v1, v2), v3))| {
|
||||
f(acc, compute(I::from(from + local), v1, v2, v3))
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn collect_one_at(&self, index: usize) -> Option<T> {
|
||||
if index >= self.len() {
|
||||
return None;
|
||||
}
|
||||
let v1 = self.source1.collect_one_at(index)?;
|
||||
let v2 = self.source2.collect_one_at(index)?;
|
||||
let v3 = self.source3.collect_one_at(index)?;
|
||||
Some((self.compute)(I::from(index), v1, v2, v3))
|
||||
}
|
||||
|
||||
fn read_sorted_into_at(&self, indices: &[usize], out: &mut Vec<T>) {
|
||||
let compute = self.compute;
|
||||
let vals1 = self.source1.read_sorted_at(indices);
|
||||
let vals2 = self.source2.read_sorted_at(indices);
|
||||
let vals3 = self.source3.read_sorted_at(indices);
|
||||
out.reserve(vals1.len().min(vals2.len()).min(vals3.len()));
|
||||
indices
|
||||
.iter()
|
||||
.zip(vals1.into_iter().zip(vals2).zip(vals3))
|
||||
.for_each(|(&i, ((v1, v2), v3))| out.push(compute(I::from(i), v1, v2, v3)));
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
use crate::{TypedVec, VecIndex, VecValue};
|
||||
|
||||
use super::LazyVecFrom3;
|
||||
|
||||
impl<I, T, S1I, S1T, S2I, S2T, S3I, S3T> TypedVec
|
||||
for LazyVecFrom3<I, T, S1I, S1T, S2I, S2T, S3I, S3T>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: VecValue,
|
||||
S1I: VecIndex,
|
||||
S1T: VecValue,
|
||||
S2I: VecIndex,
|
||||
S2T: VecValue,
|
||||
S3I: VecIndex,
|
||||
S3T: VecValue,
|
||||
{
|
||||
type I = I;
|
||||
type T = T;
|
||||
}
|
||||
@@ -1,11 +1,7 @@
|
||||
mod agg;
|
||||
mod delta;
|
||||
mod from1;
|
||||
mod from2;
|
||||
mod from3;
|
||||
mod vec;
|
||||
|
||||
pub use agg::*;
|
||||
pub use delta::*;
|
||||
pub use from1::*;
|
||||
pub use from2::*;
|
||||
pub use from3::*;
|
||||
pub use vec::*;
|
||||
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
use crate::{AnyVec, VecIndex, VecValue, Version, short_type_name};
|
||||
|
||||
use super::LazyVecFrom1;
|
||||
use super::LazyVec;
|
||||
|
||||
impl<I, T, S1I, S1T> AnyVec for LazyVecFrom1<I, T, S1I, S1T>
|
||||
impl<I, T, S1I, S1T> AnyVec for LazyVec<I, T, S1I, S1T>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: VecValue,
|
||||
+7
-9
@@ -10,8 +10,6 @@ pub use transform::*;
|
||||
|
||||
use crate::{ReadableBoxedVec, VecIndex, VecValue, Version};
|
||||
|
||||
pub type ComputeFrom1<I, T, S1T> = fn(I, S1T) -> T;
|
||||
|
||||
/// Lazily computed vector deriving values on-the-fly from one source vector.
|
||||
///
|
||||
/// Unlike `EagerVec`, no data is stored on disk. Values are computed during
|
||||
@@ -22,7 +20,7 @@ pub type ComputeFrom1<I, T, S1T> = fn(I, S1T) -> T;
|
||||
///
|
||||
/// For frequently accessed derived data, prefer `EagerVec` for better performance.
|
||||
#[derive(Clone)]
|
||||
pub struct LazyVecFrom1<I, T, S1I, S1T>
|
||||
pub struct LazyVec<I, T, S1I, S1T>
|
||||
where
|
||||
S1I: VecIndex,
|
||||
S1T: VecValue,
|
||||
@@ -30,10 +28,10 @@ where
|
||||
pub(super) name: Arc<str>,
|
||||
pub(super) base_version: Version,
|
||||
pub(super) source: ReadableBoxedVec<S1I, S1T>,
|
||||
pub(super) compute: ComputeFrom1<I, T, S1T>,
|
||||
pub(super) compute: fn(I, S1T) -> T,
|
||||
}
|
||||
|
||||
impl<I, T, S1I, S1T> LazyVecFrom1<I, T, S1I, S1T>
|
||||
impl<I, T, S1I, S1T> LazyVec<I, T, S1I, S1T>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: VecValue,
|
||||
@@ -44,12 +42,12 @@ where
|
||||
name: &str,
|
||||
version: Version,
|
||||
source: ReadableBoxedVec<S1I, S1T>,
|
||||
compute: ComputeFrom1<I, T, S1T>,
|
||||
compute: fn(I, S1T) -> T,
|
||||
) -> Self {
|
||||
assert_eq!(
|
||||
I::to_string(),
|
||||
S1I::to_string(),
|
||||
"LazyVecFrom1 index type mismatch: expected {}, got {}",
|
||||
"LazyVec index type mismatch: expected {}, got {}",
|
||||
I::to_string(),
|
||||
S1I::to_string()
|
||||
);
|
||||
@@ -63,14 +61,14 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, T, S1T> LazyVecFrom1<I, T, I, S1T>
|
||||
impl<I, T, S1T> LazyVec<I, T, I, S1T>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: VecValue,
|
||||
S1T: VecValue,
|
||||
{
|
||||
/// Create a lazy vec with a generic transform.
|
||||
/// Usage: `LazyVecFrom1::transformed::<Negate>(name, v, source)`
|
||||
/// Usage: `LazyVec::transformed::<Negate>(name, v, source)`
|
||||
pub fn transformed<F: UnaryTransform<S1T, T>>(
|
||||
name: &str,
|
||||
version: Version,
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
use crate::{ReadOnlyClone, VecIndex, VecValue};
|
||||
|
||||
use super::LazyVecFrom1;
|
||||
use super::LazyVec;
|
||||
|
||||
impl<I, T, S1I, S1T> ReadOnlyClone for LazyVecFrom1<I, T, S1I, S1T>
|
||||
impl<I, T, S1I, S1T> ReadOnlyClone for LazyVec<I, T, S1I, S1T>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: VecValue,
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
use crate::{AnyVec, ReadableVec, VecIndex, VecValue};
|
||||
|
||||
use super::LazyVecFrom1;
|
||||
use super::LazyVec;
|
||||
|
||||
impl<I, T, S1I, S1T> ReadableVec<I, T> for LazyVecFrom1<I, T, S1I, S1T>
|
||||
impl<I, T, S1I, S1T> ReadableVec<I, T> for LazyVec<I, T, S1I, S1T>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: VecValue,
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
use crate::{TypedVec, VecIndex, VecValue};
|
||||
|
||||
use super::LazyVecFrom1;
|
||||
use super::LazyVec;
|
||||
|
||||
impl<I, T, S1I, S1T> TypedVec for LazyVecFrom1<I, T, S1I, S1T>
|
||||
impl<I, T, S1I, S1T> TypedVec for LazyVec<I, T, S1I, S1T>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: VecValue,
|
||||
@@ -493,10 +493,10 @@ ancestors and no descendants (matches mempool.space).
|
||||
* Detailed series count with per-database breakdown
|
||||
*
|
||||
* @typedef {Object} DetailedSeriesCount
|
||||
* @property {number} distinctSeries - Number of unique series available (e.g., realized_price, market_cap)
|
||||
* @property {number} totalEndpoints - Total number of series-index combinations across all timeframes
|
||||
* @property {number} lazyEndpoints - Number of lazy (computed on-the-fly) series-index combinations
|
||||
* @property {number} storedEndpoints - Number of eager (stored on disk) series-index combinations
|
||||
* @property {number} distinct - Number of unique series available (e.g., realized_price, market_cap)
|
||||
* @property {number} total - Total number of series-index combinations across all timeframes
|
||||
* @property {number} lazy - Number of lazy (computed on-the-fly) series-index combinations
|
||||
* @property {number} stored - Number of eager (stored on disk) series-index combinations
|
||||
* @property {{ [key: string]: SeriesCount }} byDb - Per-database breakdown of counts
|
||||
*/
|
||||
/**
|
||||
@@ -1092,13 +1092,13 @@ on serialization otherwise.
|
||||
* @property {Limit=} limit - Maximum number of results
|
||||
*/
|
||||
/**
|
||||
* Series count statistics - distinct series and total series-index combinations
|
||||
* Series count statistics
|
||||
*
|
||||
* @typedef {Object} SeriesCount
|
||||
* @property {number} distinctSeries - Number of unique series available (e.g., realized_price, market_cap)
|
||||
* @property {number} totalEndpoints - Total number of series-index combinations across all timeframes
|
||||
* @property {number} lazyEndpoints - Number of lazy (computed on-the-fly) series-index combinations
|
||||
* @property {number} storedEndpoints - Number of eager (stored on disk) series-index combinations
|
||||
* @property {number} distinct - Number of unique series available (e.g., realized_price, market_cap)
|
||||
* @property {number} total - Total number of series-index combinations across all timeframes
|
||||
* @property {number} lazy - Number of lazy (computed on-the-fly) series-index combinations
|
||||
* @property {number} stored - Number of eager (stored on disk) series-index combinations
|
||||
*/
|
||||
/**
|
||||
* Metadata about a series
|
||||
|
||||
@@ -944,34 +944,34 @@ class DataRangeFormat(TypedDict):
|
||||
|
||||
class SeriesCount(TypedDict):
|
||||
"""
|
||||
Series count statistics - distinct series and total series-index combinations
|
||||
Series count statistics
|
||||
|
||||
Attributes:
|
||||
distinct_series: Number of unique series available (e.g., realized_price, market_cap)
|
||||
total_endpoints: Total number of series-index combinations across all timeframes
|
||||
lazy_endpoints: Number of lazy (computed on-the-fly) series-index combinations
|
||||
stored_endpoints: Number of eager (stored on disk) series-index combinations
|
||||
distinct: Number of unique series available (e.g., realized_price, market_cap)
|
||||
total: Total number of series-index combinations across all timeframes
|
||||
lazy: Number of lazy (computed on-the-fly) series-index combinations
|
||||
stored: Number of eager (stored on disk) series-index combinations
|
||||
"""
|
||||
distinct_series: int
|
||||
total_endpoints: int
|
||||
lazy_endpoints: int
|
||||
stored_endpoints: int
|
||||
distinct: int
|
||||
total: int
|
||||
lazy: int
|
||||
stored: int
|
||||
|
||||
class DetailedSeriesCount(TypedDict):
|
||||
"""
|
||||
Detailed series count with per-database breakdown
|
||||
|
||||
Attributes:
|
||||
distinct_series: Number of unique series available (e.g., realized_price, market_cap)
|
||||
total_endpoints: Total number of series-index combinations across all timeframes
|
||||
lazy_endpoints: Number of lazy (computed on-the-fly) series-index combinations
|
||||
stored_endpoints: Number of eager (stored on disk) series-index combinations
|
||||
distinct: Number of unique series available (e.g., realized_price, market_cap)
|
||||
total: Total number of series-index combinations across all timeframes
|
||||
lazy: Number of lazy (computed on-the-fly) series-index combinations
|
||||
stored: Number of eager (stored on disk) series-index combinations
|
||||
by_db: Per-database breakdown of counts
|
||||
"""
|
||||
distinct_series: int
|
||||
total_endpoints: int
|
||||
lazy_endpoints: int
|
||||
stored_endpoints: int
|
||||
distinct: int
|
||||
total: int
|
||||
lazy: int
|
||||
stored: int
|
||||
by_db: dict[str, SeriesCount]
|
||||
|
||||
class DifficultyAdjustment(TypedDict):
|
||||
|
||||
@@ -81,15 +81,16 @@ function createMaSubSection(label, averages) {
|
||||
const more = averages.filter((a) => !includes(commonMaIds, a.id));
|
||||
|
||||
/** @param {MaPeriod} a */
|
||||
const toFolder = (a) => ({
|
||||
name: periodIdToName(a.id, true),
|
||||
tree: simplePriceRatioTree({
|
||||
const toChart = (a) => {
|
||||
const name = periodIdToName(a.id, true);
|
||||
const [chart] = simplePriceRatioTree({
|
||||
pattern: a.ratio,
|
||||
title: `${periodIdToName(a.id, true)} ${label}`,
|
||||
title: `${name} ${label}`,
|
||||
legend: "Average",
|
||||
color: a.color,
|
||||
}),
|
||||
});
|
||||
});
|
||||
return { ...chart, name };
|
||||
};
|
||||
|
||||
return {
|
||||
name: label,
|
||||
@@ -106,8 +107,8 @@ function createMaSubSection(label, averages) {
|
||||
}),
|
||||
),
|
||||
},
|
||||
...common.map(toFolder),
|
||||
{ name: "More...", tree: more.map(toFolder) },
|
||||
...common.map(toChart),
|
||||
{ name: "More...", tree: more.map(toChart) },
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { brk } from "../../utils/client.js";
|
||||
import { colors } from "../../utils/colors.js";
|
||||
import { Unit } from "../../utils/units.js";
|
||||
import { histogram } from "../series.js";
|
||||
import { histogram, price } from "../series.js";
|
||||
|
||||
/**
|
||||
* Create Capital Sentiment model section.
|
||||
@@ -9,6 +9,30 @@ import { histogram } from "../series.js";
|
||||
*/
|
||||
export function createCapitalSentimentSection() {
|
||||
const { capitalSentiment } = brk.series.models;
|
||||
const { all, sth, lth } = brk.series.cohorts.utxo;
|
||||
const sma = brk.series.market.movingAverage.sma._1y;
|
||||
const references = () => [
|
||||
price({
|
||||
series: all.realized.capitalized.price,
|
||||
name: "All",
|
||||
color: colors.capitalized,
|
||||
}),
|
||||
price({
|
||||
series: sth.realized.capitalized.price,
|
||||
name: "STH",
|
||||
color: colors.term.short,
|
||||
}),
|
||||
price({
|
||||
series: lth.realized.capitalized.price,
|
||||
name: "LTH",
|
||||
color: colors.term.long,
|
||||
}),
|
||||
price({
|
||||
series: sma,
|
||||
name: "1Y SMA",
|
||||
color: colors.time._1y,
|
||||
}),
|
||||
];
|
||||
|
||||
return {
|
||||
name: "Capital Sentiment",
|
||||
@@ -16,6 +40,7 @@ export function createCapitalSentimentSection() {
|
||||
{
|
||||
name: "Score",
|
||||
title: "Capital Sentiment Score",
|
||||
top: references(),
|
||||
bottom: [
|
||||
histogram({
|
||||
series: capitalSentiment.score,
|
||||
@@ -35,6 +60,7 @@ export function createCapitalSentimentSection() {
|
||||
{
|
||||
name: "Position",
|
||||
title: "Capital Sentiment Position",
|
||||
top: references(),
|
||||
bottom: [
|
||||
histogram({
|
||||
series: capitalSentiment.isLong,
|
||||
|
||||
@@ -325,9 +325,9 @@ export function createPartialOptions() {
|
||||
{
|
||||
name: "Models",
|
||||
tree: [
|
||||
createCapitalSentimentSection(),
|
||||
createRarityMeterSection(),
|
||||
createBedrockSection(),
|
||||
createRarityMeterSection(),
|
||||
createCapitalSentimentSection(),
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user