-
Notifications
You must be signed in to change notification settings - Fork 6
/
spend_index.rs
122 lines (88 loc) · 2.89 KB
/
spend_index.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
//! Index that stores transactions and spend outputs
//!
//! This serves as a broom-wagon for the spend-tree.
//! After X blocks, outputs and transactions are stored here,
//! and when outputs aren't found in the spend tree for X blocks,
//! they are searched here.
//!
//! The data-structure here is a simple bit-index where each transaction and each spend-output
//! are given a unique bit which is set if the given transaction or spend exists
use std::sync::atomic::{AtomicU64,Ordering};
use config;
use store::flatfileset::FlatFileSet;
use store::RecordPtr;
const MB: u64 = 1024 * 1024;
const FILE_SIZE: u64 = 16 * 1024 * MB ;
const MAX_CONTENT_SIZE: u64 = FILE_SIZE - 10 * MB ;
// TODO; make this dynamic using fileset continuation;
// this isn't hastily needed as the OS does not actually allocate
// all the space; (compare ls with du).
const VEC_SIZE: usize = 500_000_000;
/// Index to lookup spends
///
/// Internally uses fileset
///
pub struct SpendIndex {
#[allow(dead_code)]
fileset: FlatFileSet<RecordPtr>,
bitvector: &'static [AtomicU64]
}
unsafe impl Sync for SpendIndex {}
impl SpendIndex
{
/// Opens the spend_index at the location given in the config
///
/// Creates a new fileset if needed
pub fn new(cfg: &config::Config) -> SpendIndex {
let dir = &cfg.root.clone().join("spend-index");
let mut fileset = FlatFileSet::new(
dir, "si-", FILE_SIZE, MAX_CONTENT_SIZE);
let bitvector = fileset.read_mut_slice(RecordPtr::new(0), VEC_SIZE);
SpendIndex {
fileset: fileset,
bitvector: bitvector
}
}
/// Tests if the given hash exists.
pub fn exists(&self, hash: u64) -> bool {
let idx = (hash >> 6) as usize;
(self.bitvector[idx].load(Ordering::Relaxed) & (1 << (hash & 0x3F))) > 0
}
/// Stores a record hash; this should uniquely identify an output or a transaction
pub fn set(&mut self, hash: u64) {
// CAS-loop
loop {
let idx = (hash >> 6) as usize;
let org = self.bitvector[idx].load(Ordering::Acquire);
let new = org | (1 << (hash & 0x3F));
if self.bitvector[idx].compare_and_swap(org, new, Ordering::Release) == org {
break;
}
}
}
}
#[cfg(test)]
mod tests {
extern crate rand;
use std::collections::HashSet;
use super::*;
#[test]
fn test_seq() {
let mut idx: SpendIndex = SpendIndex::new(& test_cfg!() );
let mut set = HashSet::new();
for n in 0..60000_u64 {
if n % 3 == 0 {
set.insert(n);
idx.set(n);
}
}
for n in 0..60000 {
if n % 3 == 0 {
assert!( idx.exists(n));
}
else {
assert!( !idx.exists(n));
}
}
}
}