mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-06-11 07:23:32 -07:00
bindex: snapshot
This commit is contained in:
Generated
+64
-3660
File diff suppressed because it is too large
Load Diff
+40
-36
@@ -1,39 +1,43 @@
|
||||
[package]
|
||||
name = "kibo_money"
|
||||
version = "0.6.0"
|
||||
edition = "2021"
|
||||
# [package]
|
||||
# name = "kibo_money"
|
||||
# version = "0.6.0"
|
||||
# edition = "2021"
|
||||
|
||||
[workspace]
|
||||
members = ["bindex", "biter", "iterable", "snkrj", "storable_vec"]
|
||||
resolver = "2"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
allocative = "0.3.4"
|
||||
axum = "0.7.9"
|
||||
bincode = { git = "https://github.com/bincode-org/bincode.git", features = [
|
||||
"serde",
|
||||
] }
|
||||
bitcoin_hashes = { version = "0.15.0" }
|
||||
biter = { path = "./src/crates/biter" }
|
||||
chrono = { version = "0.4.39", features = ["serde"] }
|
||||
clap = { version = "4.5.26", features = ["derive"] }
|
||||
color-eyre = "0.6.3"
|
||||
ctrlc = { version = "3.4.5", features = ["termination"] }
|
||||
derive_deref = "1.1.1"
|
||||
env_logger = "0.11.6"
|
||||
inferno = "0.12.1"
|
||||
itertools = "0.13.0"
|
||||
log = { version = "0.4.25", features = ["std", "serde"] }
|
||||
ordered-float = "4.6.0"
|
||||
rayon = "1.10.0"
|
||||
regex = "1.11.1"
|
||||
reqwest = { version = "0.12.12", features = ["blocking", "json"] }
|
||||
rlimit = "0.10.2"
|
||||
snkrj = { path = "./src/crates/snkrj" }
|
||||
serde = { version = "1.0.217", features = ["derive"] }
|
||||
serde_json = "1.0.135"
|
||||
struct_iterable = { path = "./src/crates/iterable" }
|
||||
swc = "9.0.2"
|
||||
swc_common = "5.0.0"
|
||||
tokio = { version = "1.43.0", features = ["full"] }
|
||||
toml = "0.8.19"
|
||||
tower-http = { version = "0.6.2", features = ["compression-full"] }
|
||||
zstd = "0.13.2"
|
||||
# [dependencies]
|
||||
# allocative = "0.3.4"
|
||||
# axum = "0.7.9"
|
||||
# bincode = { git = "https://github.com/bincode-org/bincode.git", features = [
|
||||
# "serde",
|
||||
# ] }
|
||||
# bitcoin_hashes = { version = "0.15.0" }
|
||||
# biter = { path = "./src/crates/biter" }
|
||||
# chrono = { version = "0.4.39", features = ["serde"] }
|
||||
# clap = { version = "4.5.26", features = ["derive"] }
|
||||
# color-eyre = "0.6.3"
|
||||
# ctrlc = { version = "3.4.5", features = ["termination"] }
|
||||
# derive_deref = "1.1.1"
|
||||
# env_logger = "0.11.6"
|
||||
# inferno = "0.12.1"
|
||||
# itertools = "0.13.0"
|
||||
# log = { version = "0.4.25", features = ["std", "serde"] }
|
||||
# ordered-float = "4.6.0"
|
||||
# rayon = "1.10.0"
|
||||
# regex = "1.11.1"
|
||||
# reqwest = { version = "0.12.12", features = ["blocking", "json"] }
|
||||
# rlimit = "0.10.2"
|
||||
# snkrj = { path = "./src/crates/snkrj" }
|
||||
# serde = { version = "1.0.217", features = ["derive"] }
|
||||
# serde_json = "1.0.135"
|
||||
# struct_iterable = { path = "./src/crates/iterable" }
|
||||
# swc = "9.0.2"
|
||||
# swc_common = "5.0.0"
|
||||
# tokio = { version = "1.43.0", features = ["full"] }
|
||||
# toml = "0.8.19"
|
||||
# tower-http = { version = "0.6.2", features = ["compression-full"] }
|
||||
# zstd = "0.13.2"
|
||||
|
||||
@@ -29,7 +29,8 @@ enum TxInOrAddressindextoutindex<'a> {
|
||||
}
|
||||
|
||||
const UNSAFE_BLOCKS: u32 = 100;
|
||||
const SNAPSHOT_BLOCK_RANGE: usize = 4_200; // MUST 210_000 % THIS == 0
|
||||
const DAILY_BLOCK_TARGET: usize = 144;
|
||||
const SNAPSHOT_BLOCK_RANGE: usize = DAILY_BLOCK_TARGET * 10;
|
||||
|
||||
fn main() -> color_eyre::Result<()> {
|
||||
color_eyre::install()?;
|
||||
@@ -38,7 +39,7 @@ fn main() -> color_eyre::Result<()> {
|
||||
|
||||
let check_collisions = true;
|
||||
|
||||
let data_dir = Path::new("../../../../bitcoin");
|
||||
let data_dir = Path::new("../../bitcoin");
|
||||
let cookie = Path::new(data_dir).join(".cookie");
|
||||
let rpc = Client::new("http://localhost:8332", Auth::CookieFile(cookie))?;
|
||||
|
||||
@@ -62,59 +63,63 @@ fn main() -> color_eyre::Result<()> {
|
||||
let mut txindex = vecs
|
||||
.height_to_first_txindex
|
||||
.get(height)?
|
||||
.cloned()
|
||||
.map(|v| *v)
|
||||
.unwrap_or(Txindex::default());
|
||||
|
||||
let mut txoutindex = vecs
|
||||
.height_to_first_txoutindex
|
||||
.get(height)?
|
||||
.cloned()
|
||||
.map(|v| *v)
|
||||
.unwrap_or(Txoutindex::default());
|
||||
|
||||
let mut addressindex = vecs
|
||||
.height_to_first_addressindex
|
||||
.get(height)?
|
||||
.cloned()
|
||||
.map(|v| *v)
|
||||
.unwrap_or(Addressindex::default());
|
||||
|
||||
let export = |stores: Stores, vecs: &mut Vecs, height: Height| -> color_eyre::Result<()> {
|
||||
exit.block();
|
||||
println!("Exporting...");
|
||||
// Memory: 2.87
|
||||
// Real Memory: 16.23
|
||||
// Private Memory: 10.8
|
||||
if height > Height::from(400_000_u32) {
|
||||
pause();
|
||||
}
|
||||
vecs.reset_cache();
|
||||
println!("Resetted cache");
|
||||
// Memory: 2.87
|
||||
// Real Memory: 13.24
|
||||
// Private Memory: 10.8
|
||||
if height > Height::from(400_000_u32) {
|
||||
pause();
|
||||
}
|
||||
// Memory: 3.76 GB
|
||||
// Real Memory: 22.47 GB
|
||||
// Private Memory: 12.44 GB
|
||||
// if height > Height::from(400_000_u32) {
|
||||
// pause();
|
||||
// }
|
||||
// vecs.reset_cache();
|
||||
// At: 403200
|
||||
// Memory: 3.78 GB
|
||||
// Real Memory: 12.65 GB
|
||||
// Private Memory: 11.39 GB
|
||||
// if height > Height::from(400_000_u32) {
|
||||
// pause();
|
||||
// }
|
||||
vecs.flush(height)?;
|
||||
println!("Vecs flushed");
|
||||
// Memory: 3.36
|
||||
// Real Memory: 13.55
|
||||
// Private Memory: 10.66
|
||||
// At: 403200
|
||||
// Memory: 3.79 GB
|
||||
// Real Memory: 12.37 GB
|
||||
// Private Memory: 10.95 GB
|
||||
// Gone up wtf
|
||||
if height > Height::from(400_000_u32) {
|
||||
pause();
|
||||
}
|
||||
// if height > Height::from(400_000_u32) {
|
||||
// pause();
|
||||
// }
|
||||
stores.export(height);
|
||||
println!("Export done");
|
||||
if height > Height::from(400_000_u32) {
|
||||
pause();
|
||||
}
|
||||
// At: 403200
|
||||
// Memory: 2.23 GB
|
||||
// Real Memory: 1.05 GB
|
||||
// Private Memory: 0.109 GB
|
||||
// if height > Height::from(400_000_u32) {
|
||||
// pause();
|
||||
// }
|
||||
exit.unblock();
|
||||
Ok(())
|
||||
};
|
||||
|
||||
let mut stores_opt = Some(stores);
|
||||
|
||||
biter::new(data_dir, Some(height.into()), None, rpc)
|
||||
biter::new(data_dir, Some(height.into()), Some(400_000), rpc)
|
||||
.iter()
|
||||
.try_for_each(|(_height, block, blockhash)| -> color_eyre::Result<()> {
|
||||
println!("Processing block {_height}...");
|
||||
@@ -126,7 +131,8 @@ fn main() -> color_eyre::Result<()> {
|
||||
let mut stores = stores_opt.take().context("option should have wtx")?;
|
||||
|
||||
if let Some(saved_blockhash) = vecs.height_to_blockhash.get(height)? {
|
||||
if &blockhash != saved_blockhash {
|
||||
// if &blockhash != saved_blockhash {
|
||||
if &blockhash != saved_blockhash.as_ref() {
|
||||
todo!("Rollback not implemented");
|
||||
// parts.rollback_from(&mut wtx, height, &exit)?;
|
||||
}
|
||||
@@ -162,7 +168,7 @@ fn main() -> color_eyre::Result<()> {
|
||||
tx.output
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(move |(vout, txout)| (Txindex::from(index), vout as u32, txout))
|
||||
.map(move |(vout, txout)| (Txindex::from(index), vout as u32, txout, tx))
|
||||
}).collect::<Vec<_>>();
|
||||
|
||||
let tx_len = block.txdata.len();
|
||||
@@ -272,7 +278,7 @@ fn main() -> color_eyre::Result<()> {
|
||||
outputs.into_par_iter().enumerate()
|
||||
.map(
|
||||
#[allow(clippy::type_complexity)]
|
||||
|(block_txoutindex, (block_txindex, vout, txout))| -> color_eyre::Result<(Txoutindex,
|
||||
|(block_txoutindex, (block_txindex, vout, txout, tx))| -> color_eyre::Result<(Txoutindex,
|
||||
(&TxOut, Txindexvout, Addresstype, color_eyre::Result<Addressbytes>, Option<Addressindex>))> {
|
||||
let txindex_local = txindex + block_txindex;
|
||||
let txindexvout = Txindexvout::from((txindex_local, vout));
|
||||
@@ -294,25 +300,28 @@ fn main() -> color_eyre::Result<()> {
|
||||
// Checking if not in the future
|
||||
.and_then(|addressindex_local| (addressindex_local < addressindex)
|
||||
.then_some(addressindex_local))
|
||||
});
|
||||
}); // OK
|
||||
|
||||
if let Some(Some(addressindex)) = check_collisions.then_some(addressindex_opt) {
|
||||
if let Some(Some(addressindex_local)) = check_collisions.then_some(addressindex_opt) {
|
||||
let addressbytes = addressbytes_res.as_ref().unwrap();
|
||||
|
||||
let prev_addresstype = *vecs.addressindex_to_addresstype.get(
|
||||
addressindex,
|
||||
addressindex_local,
|
||||
)?.context("Expect to have address type")?;
|
||||
|
||||
let addresstypeindex = *vecs.addressindex_to_addresstypeindex.get(
|
||||
addressindex,
|
||||
addressindex_local,
|
||||
)?.context("Expect to have address type index")?;
|
||||
// Good first time
|
||||
// Wrong after rerun
|
||||
|
||||
let prev_addressbytes_opt= vecs.get_addressbytes(prev_addresstype, addresstypeindex)?;
|
||||
|
||||
let prev_addressbytes = prev_addressbytes_opt.as_ref().context("Expect to have addressbytes")?;
|
||||
|
||||
if (vecs.addressindex_to_addresstype.hasnt(addressindex) && addresstype != prev_addresstype) || (stores.addressbytes_prefix_to_addressindex.needs(height) && prev_addressbytes != addressbytes) {
|
||||
dbg!(addresstype, prev_addresstype, prev_addressbytes, addressbytes, addressindex, addresstypeindex, txout, AddressbytesPrefix::from((addressbytes, addresstype)), AddressbytesPrefix::from((prev_addressbytes, prev_addresstype)));
|
||||
if (vecs.addressindex_to_addresstype.hasnt(addressindex_local) && addresstype != prev_addresstype) || (stores.addressbytes_prefix_to_addressindex.needs(height) && prev_addressbytes != addressbytes) {
|
||||
let txid = tx.compute_txid();
|
||||
dbg!(_height, txid, vout, block_txindex, addresstype, prev_addresstype, prev_addressbytes, addressbytes, addressindex, addressindex_local, addresstypeindex, txout, AddressbytesPrefix::from((addressbytes, addresstype)), AddressbytesPrefix::from((prev_addressbytes, prev_addresstype)));
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
@@ -373,7 +382,7 @@ fn main() -> color_eyre::Result<()> {
|
||||
|
||||
let mut addressindex_local = addressindex;
|
||||
|
||||
let mut addressbytes_prefix= None;
|
||||
let mut addressbytes_prefix = None;
|
||||
|
||||
if let Some(addressindex) = addressindex_opt.or_else(|| addressbytes_res.as_ref().ok().and_then(|addressbytes| {
|
||||
// Check if address was first seen before in this iterator
|
||||
@@ -397,6 +406,22 @@ fn main() -> color_eyre::Result<()> {
|
||||
if let Ok(addressbytes) = addressbytes_res {
|
||||
let addressbytes_prefix = addressbytes_prefix.unwrap();
|
||||
|
||||
// if addressindex_local == Addressindex::from(257905_u32) || addressbytes_prefix == AddressbytesPrefix::from(
|
||||
// [
|
||||
// 116_u8,
|
||||
// 86,
|
||||
// 96,
|
||||
// 52,
|
||||
// 2,
|
||||
// 87,
|
||||
// 151,
|
||||
// 177,
|
||||
// ],
|
||||
// ) {
|
||||
// dbg!(addressindex_local, addressbytes, addressbytes_prefix, addresstypeindex);
|
||||
// panic!();
|
||||
// }
|
||||
|
||||
already_added_addressbytes_prefix.insert(addressbytes_prefix.clone(), addressindex_local);
|
||||
|
||||
stores.addressbytes_prefix_to_addressindex.insert_if_needed(
|
||||
@@ -491,6 +516,10 @@ fn main() -> color_eyre::Result<()> {
|
||||
dbg!(txindex_local, txid, len);
|
||||
})?;
|
||||
|
||||
// #[allow(clippy::redundant_locals)]
|
||||
// let prev_txid = prev_txid;
|
||||
let prev_txid = prev_txid.as_ref();
|
||||
|
||||
// If another Txid needs to be added to the list
|
||||
// We need to check that it's also a coinbase tx otherwise par_iter inputs needs to be updated
|
||||
let only_known_dup_txids = [
|
||||
@@ -539,17 +568,13 @@ fn main() -> color_eyre::Result<()> {
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
dbg!(i.elapsed());
|
||||
|
||||
pause();
|
||||
|
||||
let stores = stores_opt.take().context("option should have wtx")?;
|
||||
export(stores, &mut vecs, height)?;
|
||||
|
||||
pause();
|
||||
|
||||
dbg!(i.elapsed());
|
||||
|
||||
pause();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ use jiff::tz::TimeZone;
|
||||
|
||||
use super::Timestamp;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Date(jiff::civil::Date);
|
||||
|
||||
impl From<&Timestamp> for Date {
|
||||
@@ -28,6 +28,11 @@ impl From<(&Addressbytes, Addresstype)> for AddressbytesPrefix {
|
||||
))
|
||||
}
|
||||
}
|
||||
impl From<[u8; 8]> for AddressbytesPrefix {
|
||||
fn from(value: [u8; 8]) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deref, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct BlockHashPrefix([u8; 8]);
|
||||
@@ -16,7 +16,7 @@ pub struct StorableVec<I, T> {
|
||||
impl<I, T> StorableVec<I, T>
|
||||
where
|
||||
I: Into<usize>,
|
||||
T: Sized + Debug,
|
||||
T: Sized + Debug + Clone,
|
||||
{
|
||||
pub fn import(path: &Path, version: Version) -> io::Result<Self> {
|
||||
fs::create_dir_all(path)?;
|
||||
@@ -70,10 +70,6 @@ where
|
||||
path.join("height")
|
||||
}
|
||||
|
||||
fn reset_cache(&mut self) {
|
||||
self.vec.reset_cache();
|
||||
}
|
||||
|
||||
// pub fn needs(&self, height: Height) -> bool {
|
||||
// self.height() // store height in struct
|
||||
// }
|
||||
@@ -93,23 +89,18 @@ impl<I, T> DerefMut for StorableVec<I, T> {
|
||||
|
||||
pub trait AnyBindexVec {
|
||||
fn height(&self) -> color_eyre::Result<Height>;
|
||||
fn reset_cache(&mut self);
|
||||
fn flush(&mut self, height: Height) -> io::Result<()>;
|
||||
}
|
||||
|
||||
impl<I, T> AnyBindexVec for StorableVec<I, T>
|
||||
where
|
||||
I: Into<usize>,
|
||||
T: Sized + Debug,
|
||||
T: Sized + Debug + Clone,
|
||||
{
|
||||
fn height(&self) -> color_eyre::Result<Height> {
|
||||
self.height()
|
||||
}
|
||||
|
||||
fn reset_cache(&mut self) {
|
||||
self.reset_cache();
|
||||
}
|
||||
|
||||
fn flush(&mut self, height: Height) -> io::Result<()> {
|
||||
self.flush(height)
|
||||
}
|
||||
@@ -168,38 +168,38 @@ impl Vecs {
|
||||
Addresstype::P2PK65 => self
|
||||
.p2pk65index_to_p2pk65addressbytes
|
||||
.get(addresstypeindex)?
|
||||
.cloned()
|
||||
.map(Addressbytes::from),
|
||||
// .map(|v| Addressbytes::from(v.clone())),
|
||||
.map(|v| Addressbytes::from(v.into_inner())),
|
||||
Addresstype::P2PK33 => self
|
||||
.p2pk33index_to_p2pk33addressbytes
|
||||
.get(addresstypeindex)?
|
||||
.cloned()
|
||||
.map(Addressbytes::from),
|
||||
// .map(|v| Addressbytes::from(v.clone())),
|
||||
.map(|v| Addressbytes::from(v.into_inner())),
|
||||
Addresstype::P2PKH => self
|
||||
.p2pkhindex_to_p2pkhaddressbytes
|
||||
.get(addresstypeindex)?
|
||||
.cloned()
|
||||
.map(Addressbytes::from),
|
||||
// .map(|v| Addressbytes::from(v.clone())),
|
||||
.map(|v| Addressbytes::from(v.into_inner())),
|
||||
Addresstype::P2SH => self
|
||||
.p2shindex_to_p2shaddressbytes
|
||||
.get(addresstypeindex)?
|
||||
.cloned()
|
||||
.map(Addressbytes::from),
|
||||
// .map(|v| Addressbytes::from(v.clone())),
|
||||
.map(|v| Addressbytes::from(v.into_inner())),
|
||||
Addresstype::P2WPKH => self
|
||||
.p2wpkhindex_to_p2wpkhaddressbytes
|
||||
.get(addresstypeindex)?
|
||||
.cloned()
|
||||
.map(Addressbytes::from),
|
||||
// .map(|v| Addressbytes::from(v.clone())),
|
||||
.map(|v| Addressbytes::from(v.into_inner())),
|
||||
Addresstype::P2WSH => self
|
||||
.p2wshindex_to_p2wshaddressbytes
|
||||
.get(addresstypeindex)?
|
||||
.cloned()
|
||||
.map(Addressbytes::from),
|
||||
// .map(|v| Addressbytes::from(v.clone())),
|
||||
.map(|v| Addressbytes::from(v.into_inner())),
|
||||
Addresstype::P2TR => self
|
||||
.p2trindex_to_p2traddressbytes
|
||||
.get(addresstypeindex)?
|
||||
.cloned()
|
||||
.map(Addressbytes::from),
|
||||
// .map(|v| Addressbytes::from(v.clone())),
|
||||
.map(|v| Addressbytes::from(v.into_inner())),
|
||||
_ => unreachable!(),
|
||||
})
|
||||
}
|
||||
@@ -324,12 +324,6 @@ impl Vecs {
|
||||
.try_for_each(|vec| vec.flush(height))
|
||||
}
|
||||
|
||||
pub fn reset_cache(&mut self) {
|
||||
self.as_mut_slice().par_iter_mut().for_each(|vec| {
|
||||
vec.reset_cache();
|
||||
})
|
||||
}
|
||||
|
||||
pub fn min_height(&self) -> color_eyre::Result<Option<Height>> {
|
||||
Ok(self
|
||||
.as_slice()
|
||||
@@ -58,26 +58,24 @@ enum BlockState {
|
||||
///
|
||||
/// use bitcoincore_rpc::{Auth, Client};
|
||||
///
|
||||
/// fn main() {
|
||||
/// let i = std::time::Instant::now();
|
||||
/// let i = std::time::Instant::now();
|
||||
///
|
||||
/// let data_dir = Path::new("../../bitcoin");
|
||||
/// let url = "http://localhost:8332";
|
||||
/// let cookie = Path::new(data_dir).join(".cookie");
|
||||
/// let auth = Auth::CookieFile(cookie);
|
||||
/// let rpc = Client::new(url, auth).unwrap();
|
||||
/// let data_dir = Path::new("../../bitcoin");
|
||||
/// let url = "http://localhost:8332";
|
||||
/// let cookie = Path::new(data_dir).join(".cookie");
|
||||
/// let auth = Auth::CookieFile(cookie);
|
||||
/// let rpc = Client::new(url, auth).unwrap();
|
||||
///
|
||||
/// let start = Some(850_000);
|
||||
/// let end = None;
|
||||
/// let start = Some(850_000);
|
||||
/// let end = None;
|
||||
///
|
||||
/// biter::new(data_dir, start, end, rpc)
|
||||
/// .iter()
|
||||
/// .for_each(|(height, _block, hash)| {
|
||||
/// println!("{height}: {hash}");
|
||||
/// });
|
||||
/// biter::new(data_dir, start, end, rpc)
|
||||
/// .iter()
|
||||
/// .for_each(|(height, _block, hash)| {
|
||||
/// println!("{height}: {hash}");
|
||||
/// });
|
||||
///
|
||||
/// dbg!(i.elapsed());
|
||||
/// }
|
||||
/// dbg!(i.elapsed());
|
||||
/// ```
|
||||
///
|
||||
pub fn new(
|
||||
@@ -285,7 +283,7 @@ pub fn new(
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
if end.map_or(false, |end| height == end) {
|
||||
if end == Some(height) {
|
||||
return ControlFlow::Break(());
|
||||
}
|
||||
|
||||
@@ -2,11 +2,11 @@ use std::{
|
||||
cmp::Ordering,
|
||||
fmt::{self, Debug},
|
||||
fs::{File, OpenOptions},
|
||||
io::{self, Write},
|
||||
io::{self, Read, Seek, SeekFrom, Write},
|
||||
marker::PhantomData,
|
||||
mem,
|
||||
ops::Range,
|
||||
path::Path,
|
||||
ops::{Deref, Range},
|
||||
path::{Path, PathBuf},
|
||||
sync::OnceLock,
|
||||
};
|
||||
|
||||
@@ -25,6 +25,8 @@ use memmap2::{Mmap, MmapOptions};
|
||||
///
|
||||
#[derive(Debug)]
|
||||
pub struct StorableVec<I, T> {
|
||||
pathbuf: PathBuf,
|
||||
// file_for_reads: File,
|
||||
file: File,
|
||||
cache: Vec<OnceLock<Box<Mmap>>>, // Boxed to reduce the size of the lock (24 > 16)
|
||||
disk_len: usize,
|
||||
@@ -36,28 +38,69 @@ pub struct StorableVec<I, T> {
|
||||
}
|
||||
|
||||
/// In bytes
|
||||
const MAX_PAGE_SIZE: usize = 4096;
|
||||
const ONE_MB: usize = 1024 * 1024;
|
||||
const MAX_CACHE_SIZE: usize = 50 * ONE_MB;
|
||||
const MAX_PAGE_SIZE: usize = 4 * 4096;
|
||||
const ONE_MB: usize = 1000 * 1024;
|
||||
const MAX_CACHE_SIZE: usize = 100 * ONE_MB;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Value<'a, T> {
|
||||
Ref(&'a T),
|
||||
Owned(T),
|
||||
}
|
||||
|
||||
impl<T> Value<'_, T>
|
||||
where
|
||||
T: Sized + Debug + Clone,
|
||||
{
|
||||
pub fn into_inner(self) -> T {
|
||||
match self {
|
||||
Self::Ref(t) => t.to_owned(),
|
||||
Self::Owned(t) => t,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for Value<'_, T> {
|
||||
type Target = T;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
match self {
|
||||
Self::Ref(t) => t,
|
||||
Self::Owned(t) => t,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AsRef<T> for Value<'_, T>
|
||||
where
|
||||
T: Sized + Debug + Clone,
|
||||
{
|
||||
fn as_ref(&self) -> &T {
|
||||
match self {
|
||||
Self::Ref(t) => t,
|
||||
Self::Owned(t) => t,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, T> StorableVec<I, T>
|
||||
where
|
||||
I: Into<usize>,
|
||||
T: Sized + Debug,
|
||||
T: Sized + Debug + Clone,
|
||||
{
|
||||
pub const SIZE: usize = size_of::<T>();
|
||||
|
||||
pub const PER_PAGE: usize = MAX_PAGE_SIZE / Self::SIZE;
|
||||
pub const SIZE_OF_T: usize = size_of::<T>();
|
||||
pub const PER_PAGE: usize = MAX_PAGE_SIZE / Self::SIZE_OF_T;
|
||||
/// In bytes
|
||||
pub const PAGE_SIZE: usize = Self::PER_PAGE * Self::SIZE;
|
||||
pub const PAGE_SIZE: usize = Self::PER_PAGE * Self::SIZE_OF_T;
|
||||
pub const CACHE_LENGTH: usize = MAX_CACHE_SIZE / Self::PAGE_SIZE;
|
||||
|
||||
pub fn import(path: &Path) -> Result<Self, io::Error> {
|
||||
let file = Self::open_file(path)?;
|
||||
|
||||
let mut this = Self {
|
||||
pathbuf: path.to_owned(),
|
||||
disk_len: Self::byte_index_to_index(file.metadata()?.len() as usize),
|
||||
file,
|
||||
// file_for_reads: Self::open_file(path)?,
|
||||
cache: vec![],
|
||||
pushed: vec![],
|
||||
// updated: BTreeMap::new(),
|
||||
@@ -66,15 +109,21 @@ where
|
||||
phantom: PhantomData,
|
||||
};
|
||||
|
||||
this.cache.resize_with(Self::CACHE_LENGTH, Default::default);
|
||||
this.cache.shrink_to_fit();
|
||||
|
||||
this.reset_cache();
|
||||
|
||||
Ok(this)
|
||||
}
|
||||
|
||||
pub fn reset_cache(&mut self) {
|
||||
let len = (self.disk_len as f64 / Self::PER_PAGE as f64).ceil() as usize;
|
||||
self.cache.clear();
|
||||
self.cache.resize_with(len, Default::default);
|
||||
fn reset_cache(&mut self) {
|
||||
// let len = (self.disk_len as f64 / Self::PER_PAGE as f64).ceil() as usize;
|
||||
// self.cache.clear();
|
||||
// self.cache.resize_with(len, Default::default);
|
||||
self.cache.iter_mut().for_each(|lock| {
|
||||
lock.take();
|
||||
});
|
||||
}
|
||||
|
||||
fn open_file(path: &Path) -> Result<File, io::Error> {
|
||||
@@ -87,24 +136,19 @@ where
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn index_to_mmap_index(index: usize) -> usize {
|
||||
Self::index_to_byte_index(index) / Self::PAGE_SIZE
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn index_to_range(index: usize) -> Range<usize> {
|
||||
fn index_to_byte_range(index: usize) -> Range<usize> {
|
||||
let index = Self::index_to_byte_index(index) % Self::PAGE_SIZE;
|
||||
index..(index + Self::SIZE)
|
||||
index..(index + Self::SIZE_OF_T)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn index_to_byte_index(index: usize) -> usize {
|
||||
index * Self::SIZE
|
||||
index * Self::SIZE_OF_T
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn byte_index_to_index(byte_index: usize) -> usize {
|
||||
byte_index / Self::SIZE
|
||||
byte_index / Self::SIZE_OF_T
|
||||
}
|
||||
|
||||
fn index_to_pushed_index(&self, index: usize) -> Result<Option<usize>> {
|
||||
@@ -122,14 +166,14 @@ where
|
||||
|
||||
#[allow(unused)]
|
||||
#[inline]
|
||||
pub fn get(&self, index: I) -> Result<Option<&T>> {
|
||||
pub fn get(&self, index: I) -> Result<Option<Value<'_, T>>> {
|
||||
self.get_(index.into())
|
||||
}
|
||||
pub fn get_(&self, index: usize) -> Result<Option<&T>> {
|
||||
pub fn get_(&self, index: usize) -> Result<Option<Value<'_, T>>> {
|
||||
match self.index_to_pushed_index(index) {
|
||||
Ok(index) => {
|
||||
if let Some(index) = index {
|
||||
return Ok(self.pushed.get(index));
|
||||
return Ok(self.pushed.get(index).map(|v| Value::Ref(v)));
|
||||
}
|
||||
}
|
||||
Err(Error::IndexTooHigh) => return Ok(None),
|
||||
@@ -142,35 +186,56 @@ where
|
||||
// }
|
||||
// }
|
||||
|
||||
let mmap_index = Self::index_to_mmap_index(index);
|
||||
let byte_index = Self::index_to_byte_index(index);
|
||||
let page_index = index / Self::PER_PAGE;
|
||||
let last_index = self.disk_len - 1;
|
||||
let max_page_index = last_index / Self::PER_PAGE;
|
||||
let min_page_index = (max_page_index + 1)
|
||||
.checked_sub(Self::CACHE_LENGTH)
|
||||
.unwrap_or_default();
|
||||
|
||||
let mmap = &**self
|
||||
.cache
|
||||
.get(mmap_index)
|
||||
.ok_or(Error::MmapsVecIsTooSmall)?
|
||||
.get_or_init(|| {
|
||||
Box::new(unsafe {
|
||||
MmapOptions::new()
|
||||
.len(MAX_PAGE_SIZE)
|
||||
.offset((mmap_index * Self::PAGE_SIZE) as u64)
|
||||
.map(&self.file)
|
||||
.unwrap()
|
||||
})
|
||||
});
|
||||
if page_index >= min_page_index {
|
||||
let mmap = &**self
|
||||
.cache
|
||||
.get(page_index - min_page_index)
|
||||
.ok_or(Error::MmapsVecIsTooSmall)?
|
||||
.get_or_init(|| {
|
||||
Box::new(unsafe {
|
||||
MmapOptions::new()
|
||||
.len(Self::PAGE_SIZE)
|
||||
.offset((page_index * Self::PAGE_SIZE) as u64)
|
||||
.map(&self.file)
|
||||
.unwrap()
|
||||
})
|
||||
});
|
||||
|
||||
let range = Self::index_to_range(index);
|
||||
let src = &mmap[range];
|
||||
let range = Self::index_to_byte_range(index);
|
||||
|
||||
Ok(Some(T::unsafe_try_from_slice(src)?))
|
||||
let slice = &mmap[range];
|
||||
|
||||
Ok(Some(Value::Ref(T::unsafe_try_from_slice(slice)?)))
|
||||
} else {
|
||||
// let mut file = &self.file_for_reads;
|
||||
let mut file = Self::open_file(&self.pathbuf).unwrap();
|
||||
|
||||
file.seek(SeekFrom::Start(byte_index as u64)).unwrap();
|
||||
|
||||
let mut buf = vec![0; Self::SIZE_OF_T];
|
||||
file.read_exact(&mut buf).unwrap();
|
||||
|
||||
let value = T::unsafe_try_from_slice(&buf[..])?;
|
||||
|
||||
Ok(Some(Value::Owned(value.to_owned())))
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn first(&self) -> Result<Option<&T>> {
|
||||
pub fn first(&self) -> Result<Option<Value<'_, T>>> {
|
||||
self.get_(0)
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn last(&self) -> Result<Option<&T>> {
|
||||
pub fn last(&self) -> Result<Option<Value<'_, T>>> {
|
||||
let len = self.len();
|
||||
if len == 0 {
|
||||
return Ok(None);
|
||||
@@ -261,6 +326,7 @@ where
|
||||
|
||||
pub fn flush(&mut self) -> io::Result<()> {
|
||||
self.disk_len += self.pushed.len();
|
||||
|
||||
self.reset_cache();
|
||||
|
||||
let mut bytes: Vec<u8> = vec![];
|
||||
@@ -269,21 +335,28 @@ where
|
||||
.into_iter()
|
||||
.for_each(|v| bytes.extend_from_slice(v.unsafe_as_slice()));
|
||||
|
||||
self.file.write_all(&bytes)
|
||||
// self.file.seek(SeekFrom::End(0))?;
|
||||
self.file.write_all(&bytes)?;
|
||||
// self.file_for_mmaps.write_all(&bytes)?;
|
||||
|
||||
// self.file_for_mmaps.sync_all()?;
|
||||
// self.file_for_reads.sync_all()?;
|
||||
// self.file_for_reads = Self::open_file(&self.pathbuf)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub trait AnyStorableVec {
|
||||
fn len(&self) -> usize;
|
||||
fn is_empty(&self) -> bool;
|
||||
fn reset_cache(&mut self);
|
||||
fn flush(&mut self) -> io::Result<()>;
|
||||
}
|
||||
|
||||
impl<I, T> AnyStorableVec for StorableVec<I, T>
|
||||
where
|
||||
I: Into<usize>,
|
||||
T: Sized + Debug,
|
||||
T: Sized + Debug + Clone,
|
||||
{
|
||||
fn len(&self) -> usize {
|
||||
self.len()
|
||||
@@ -293,10 +366,6 @@ where
|
||||
self.is_empty()
|
||||
}
|
||||
|
||||
fn reset_cache(&mut self) {
|
||||
self.reset_cache();
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.flush()
|
||||
}
|
||||
Reference in New Issue
Block a user