use std::ops::Add;
use std::num::Wrapping;
use num::{Bounded, One, Zero};
use trans::{SequentialIds, TransactionIds};
const TRANSACTION_ID_PREALLOC_LEN: usize = 2048;
pub struct LocallyShuffledIds<T> {
sequential: SequentialIds<T>,
stored_ids: Vec<T>
}
impl<T> LocallyShuffledIds<T>
where T: One + Zero + Clone + Eq + Bounded + Default,
Wrapping<T>: Add<Wrapping<T>, Output = Wrapping<T>> {
pub fn new() -> LocallyShuffledIds<T> {
LocallyShuffledIds::start_at(T::zero())
}
pub fn start_at(start: T) -> LocallyShuffledIds<T> {
LocallyShuffledIds{ sequential: SequentialIds::start_at(start), stored_ids: Vec::new() }
}
fn refill_stored_ids(&mut self) {
self.stored_ids.clear();
let max_value = T::max_value();
let min_value = T::min_value();
let mut contains_min_value = false;
let mut contains_max_value = false;
let mut num_ids_generated = 0;
while num_ids_generated < TRANSACTION_ID_PREALLOC_LEN && (!contains_min_value || !contains_max_value) {
let next_id = self.sequential.generate();
contains_min_value = contains_min_value || next_id == min_value;
contains_max_value = contains_max_value || next_id == max_value;
self.stored_ids.push(next_id);
num_ids_generated += 1;
}
::fisher_shuffle(&mut self.stored_ids[..]);
}
}
impl<T> TransactionIds<T> for LocallyShuffledIds<T>
where T: One + Zero + Clone + Eq + Bounded + Default,
Wrapping<T>: Add<Wrapping<T>, Output = Wrapping<T>>{
fn generate(&mut self) -> T {
self.stored_ids.pop().unwrap_or_else(|| {
self.refill_stored_ids();
self.generate()
})
}
}
#[cfg(test)]
mod tests {
use super::LocallyShuffledIds;
use trans::TransactionIds;
#[test]
fn positive_single_prealloc_u8_overflow() {
let u8_num_values = 2u32.pow(0u8.count_zeros()) as usize;
let duplicates_to_find = super::TRANSACTION_ID_PREALLOC_LEN / u8_num_values;
let mut generator = LocallyShuffledIds::<u8>::new();
let mut tid_count = vec![0u8; u8_num_values];
for tid in (0..super::TRANSACTION_ID_PREALLOC_LEN).map(|_| generator.generate()) {
let index = tid as usize;
tid_count[index] += 1;
}
for count in tid_count.iter() {
assert_eq!(*count, duplicates_to_find as u8);
}
}
#[test]
fn positive_multiple_prealloc_u8_overflow() {
let u8_num_values = 2u32.pow(0u8.count_zeros()) as usize;
let duplicates_to_find = (super::TRANSACTION_ID_PREALLOC_LEN / u8_num_values) * 2;
let mut generator = LocallyShuffledIds::<u8>::new();
let mut tid_count = vec![0u8; u8_num_values];
for tid in (0..(super::TRANSACTION_ID_PREALLOC_LEN * 2)).map(|_| generator.generate()) {
let index = tid as usize;
tid_count[index] += 1;
}
for count in tid_count.iter() {
assert_eq!(*count, duplicates_to_find as u8);
}
}
#[test]
fn positive_single_prealloc_i8_overflow() {
let i8_num_values = 2u32.pow(0i8.count_zeros()) as usize;
let duplicates_to_find = super::TRANSACTION_ID_PREALLOC_LEN / i8_num_values;
let mut generator = LocallyShuffledIds::<u8>::new();
let mut tid_count = vec![0i8; i8_num_values];
for tid in (0..super::TRANSACTION_ID_PREALLOC_LEN).map(|_| generator.generate()) {
let index = tid as usize;
tid_count[index] += 1;
}
for count in tid_count.iter() {
assert_eq!(*count, duplicates_to_find as i8);
}
}
#[test]
fn positive_multiple_prealloc_i8_overflow() {
let i8_num_values = 2u32.pow(0i8.count_zeros()) as usize;
let duplicates_to_find = (super::TRANSACTION_ID_PREALLOC_LEN / i8_num_values) * 2;
let mut generator = LocallyShuffledIds::<u8>::new();
let mut tid_count = vec![0i8; i8_num_values];
for tid in (0..(super::TRANSACTION_ID_PREALLOC_LEN * 2)).map(|_| generator.generate()) {
let index = tid as usize;
tid_count[index] += 1;
}
for count in tid_count.iter() {
assert_eq!(*count, duplicates_to_find as i8);
}
}
#[test]
fn positive_single_prealloc_u32_no_overflow() {
let mut generator = LocallyShuffledIds::<u32>::new();
let mut tid_count = [0u8; super::TRANSACTION_ID_PREALLOC_LEN];
for tid in (0..super::TRANSACTION_ID_PREALLOC_LEN).map(|_| generator.generate()) {
let index = tid as usize;
tid_count[index] += 1;
}
for count in tid_count.iter() {
assert_eq!(*count, 1);
}
}
#[test]
fn positive_multiple_prealloc_u32_no_overflow() {
let mut generator = LocallyShuffledIds::<u32>::new();
let mut tid_count = [0u8; (super::TRANSACTION_ID_PREALLOC_LEN * 2)];
for tid in (0..(super::TRANSACTION_ID_PREALLOC_LEN * 2)).map(|_| generator.generate()) {
let index = tid as usize;
tid_count[index] += 1;
}
for count in tid_count.iter() {
assert_eq!(*count, 1);
}
}
}