Generate directories for better lookup times
This commit is contained in:
commit
eb89155365
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
/target
|
||||
Cargo.lock
|
9
Cargo.toml
Normal file
9
Cargo.toml
Normal file
|
@ -0,0 +1,9 @@
|
|||
[package]
|
||||
name = "storage-path-generator"
|
||||
version = "0.1.0"
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
parking_lot = "0.11"
|
120
src/lib.rs
Normal file
120
src/lib.rs
Normal file
|
@ -0,0 +1,120 @@
|
|||
use parking_lot::Mutex;
|
||||
use std::{convert::TryInto, sync::Arc};
|
||||
|
||||
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
|
||||
struct Inner {
|
||||
counter: Vec<u16>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Generator {
|
||||
current: Arc<Mutex<Inner>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct Path {
|
||||
inner: Vec<u16>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PathError;
|
||||
|
||||
impl Inner {
|
||||
fn next(&mut self) -> Vec<u16> {
|
||||
let mut modified_depth = 0;
|
||||
|
||||
for (depth, count) in self.counter.iter_mut().enumerate().rev() {
|
||||
if *count < 999 {
|
||||
*count += 1;
|
||||
modified_depth = depth;
|
||||
break;
|
||||
} else {
|
||||
*count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if modified_depth == 0 {
|
||||
self.counter.push(0);
|
||||
}
|
||||
|
||||
self.counter.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl Generator {
|
||||
pub fn new() -> Self {
|
||||
Self::from_existing(Path { inner: vec![] })
|
||||
}
|
||||
|
||||
pub fn from_existing(existing: Path) -> Self {
|
||||
Generator {
|
||||
current: Arc::new(Mutex::new(Inner {
|
||||
counter: existing.inner,
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn next(&self) -> Path {
|
||||
let inner = self.current.lock().next();
|
||||
|
||||
Path { inner }
|
||||
}
|
||||
}
|
||||
|
||||
impl Path {
|
||||
pub fn from_be_bytes(bytes: Vec<u8>) -> Result<Self, PathError> {
|
||||
let inner = bytes
|
||||
.chunks(2)
|
||||
.map(|chunk| {
|
||||
let be_bytes: [u8; 2] = chunk.try_into().map_err(|_| PathError)?;
|
||||
Ok(u16::from_be_bytes(be_bytes))
|
||||
})
|
||||
.collect::<Result<_, PathError>>()?;
|
||||
|
||||
Self::from_vec(inner)
|
||||
}
|
||||
|
||||
pub fn to_be_bytes(&self) -> Vec<u8> {
|
||||
self.inner
|
||||
.iter()
|
||||
.flat_map(|num| num.to_be_bytes())
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn into_inner(self) -> Vec<u16> {
|
||||
self.inner
|
||||
}
|
||||
|
||||
pub fn from_vec(inner: Vec<u16>) -> Result<Self, PathError> {
|
||||
if inner.is_empty() {
|
||||
return Ok(Path { inner });
|
||||
}
|
||||
|
||||
if inner.len().saturating_sub(1) != inner[0].into() {
|
||||
return Err(PathError);
|
||||
}
|
||||
|
||||
for elem in inner.iter() {
|
||||
if *elem > 999 {
|
||||
return Err(PathError);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Path { inner })
|
||||
}
|
||||
|
||||
pub fn to_strings(&self) -> Vec<String> {
|
||||
self.inner
|
||||
.iter()
|
||||
.map(|dir| {
|
||||
if *dir > 99 {
|
||||
format!("{}", dir)
|
||||
} else if *dir > 9 {
|
||||
format!("0{}", dir)
|
||||
} else {
|
||||
format!("00{}", dir)
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
221
tests/generator.rs
Normal file
221
tests/generator.rs
Normal file
|
@ -0,0 +1,221 @@
|
|||
use storage_path_generator::{Generator, Path};
|
||||
|
||||
#[test]
|
||||
fn invalid_paths() {
|
||||
let paths = [
|
||||
vec![0, 1],
|
||||
vec![1],
|
||||
vec![2, 1],
|
||||
vec![3, 1, 1],
|
||||
vec![1, 1000],
|
||||
vec![2, 0, 1000],
|
||||
vec![2, 1000, 0],
|
||||
];
|
||||
|
||||
for path in paths {
|
||||
assert!(Path::from_vec(path).is_err());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generates_valid_paths() {
|
||||
let generator = Generator::new();
|
||||
|
||||
for _ in 0..1_000_000 {
|
||||
let path = generator.next();
|
||||
Path::from_vec(path.into_inner()).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn round_trip() {
|
||||
const VALUES: [u16; 4] = [0, 30, 450, 999];
|
||||
|
||||
fn do_build(depth: u16, existing: Vec<u16>) -> Box<dyn Iterator<Item = Vec<u16>>> {
|
||||
if depth > 0 {
|
||||
let left = VALUES.iter().flat_map(move |value| {
|
||||
let mut new_vec = existing.clone();
|
||||
new_vec.push(*value);
|
||||
do_build(depth - 1, new_vec)
|
||||
});
|
||||
|
||||
Box::new(left)
|
||||
} else {
|
||||
Box::new(vec![existing].into_iter())
|
||||
}
|
||||
}
|
||||
|
||||
fn build(max_depth: u16) -> impl Iterator<Item = Vec<u16>> {
|
||||
(0..max_depth).flat_map(|i| do_build(i, vec![i]))
|
||||
}
|
||||
|
||||
for vec in build(8) {
|
||||
let path = Path::from_vec(vec).unwrap();
|
||||
|
||||
let bytes = path.to_be_bytes();
|
||||
let new_path = Path::from_be_bytes(bytes).unwrap();
|
||||
|
||||
assert_eq!(new_path, path);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn first_level_iter() {
|
||||
let generator = Generator::new();
|
||||
let zero = generator.next().into_inner();
|
||||
let one = generator.next().into_inner();
|
||||
let two = generator.next().into_inner();
|
||||
|
||||
let expect_zero = vec![0];
|
||||
let expect_one = vec![1, 0];
|
||||
let expect_two = vec![1, 1];
|
||||
|
||||
assert_eq!(zero, expect_zero);
|
||||
assert_eq!(one, expect_one);
|
||||
assert_eq!(two, expect_two);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn third_level_iter() {
|
||||
let generator = Generator::from_existing(Path::from_vec(vec![2, 0, 998]).unwrap());
|
||||
let zero = generator.next().into_inner();
|
||||
let one = generator.next().into_inner();
|
||||
let two = generator.next().into_inner();
|
||||
|
||||
let expect_zero = vec![2, 0, 999];
|
||||
let expect_one = vec![2, 1, 0];
|
||||
let expect_two = vec![2, 1, 1];
|
||||
|
||||
assert_eq!(zero, expect_zero);
|
||||
assert_eq!(one, expect_one);
|
||||
assert_eq!(two, expect_two);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn third_level_iter_two() {
|
||||
let generator = Generator::from_existing(Path::from_vec(vec![2, 500, 998]).unwrap());
|
||||
let zero = generator.next().to_strings();
|
||||
let one = generator.next().to_strings();
|
||||
let two = generator.next().to_strings();
|
||||
|
||||
let expect_zero = vec!["002", "500", "999"];
|
||||
let expect_one = vec!["002", "501", "000"];
|
||||
let expect_two = vec!["002", "501", "001"];
|
||||
|
||||
assert_eq!(zero, expect_zero);
|
||||
assert_eq!(one, expect_one);
|
||||
assert_eq!(two, expect_two);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fourth_level_iter() {
|
||||
let generator = Generator::from_existing(Path::from_vec(vec![3, 0, 999, 998]).unwrap());
|
||||
let zero = generator.next().to_strings();
|
||||
let one = generator.next().to_strings();
|
||||
let two = generator.next().to_strings();
|
||||
|
||||
let expect_zero = vec!["003", "000", "999", "999"];
|
||||
let expect_one = vec!["003", "001", "000", "000"];
|
||||
let expect_two = vec!["003", "001", "000", "001"];
|
||||
|
||||
assert_eq!(zero, expect_zero);
|
||||
assert_eq!(one, expect_one);
|
||||
assert_eq!(two, expect_two);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fourth_level_iter_two() {
|
||||
let generator = Generator::from_existing(Path::from_vec(vec![3, 0, 500, 998]).unwrap());
|
||||
let zero = generator.next().to_strings();
|
||||
let one = generator.next().to_strings();
|
||||
let two = generator.next().to_strings();
|
||||
|
||||
let expect_zero = vec!["003", "000", "500", "999"];
|
||||
let expect_one = vec!["003", "000", "501", "000"];
|
||||
let expect_two = vec!["003", "000", "501", "001"];
|
||||
|
||||
assert_eq!(zero, expect_zero);
|
||||
assert_eq!(one, expect_one);
|
||||
assert_eq!(two, expect_two);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn digit_transitions() {
|
||||
let generator = Generator::from_existing(Path::from_vec(vec![1, 8]).unwrap());
|
||||
|
||||
let nine = generator.next().to_strings();
|
||||
let ten = generator.next().to_strings();
|
||||
|
||||
let expect_nine = vec!["001", "009"];
|
||||
let expect_ten = vec!["001", "010"];
|
||||
|
||||
assert_eq!(nine, expect_nine);
|
||||
assert_eq!(ten, expect_ten);
|
||||
|
||||
let generator = Generator::from_existing(Path::from_vec(vec![1, 98]).unwrap());
|
||||
|
||||
let ninety_nine = generator.next().to_strings();
|
||||
let one_hundred = generator.next().to_strings();
|
||||
|
||||
let expect_ninety_nine = vec!["001", "099"];
|
||||
let expect_one_hundred = vec!["001", "100"];
|
||||
|
||||
assert_eq!(ninety_nine, expect_ninety_nine);
|
||||
assert_eq!(one_hundred, expect_one_hundred);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn second_level_transition() {
|
||||
let generator = Generator::new();
|
||||
|
||||
let first_level = generator.next().to_strings();
|
||||
let second_level = generator.next().to_strings();
|
||||
|
||||
let expect_first_level = vec!["000"];
|
||||
let expect_second_level = vec!["001", "000"];
|
||||
|
||||
assert_eq!(first_level, expect_first_level);
|
||||
assert_eq!(second_level, expect_second_level);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn third_level_transition() {
|
||||
let generator = Generator::from_existing(Path::from_vec(vec![1, 998]).unwrap());
|
||||
|
||||
let second_level = generator.next().to_strings();
|
||||
let third_level = generator.next().to_strings();
|
||||
|
||||
let expect_second_level = vec!["001", "999"];
|
||||
let expect_third_level = vec!["002", "000", "000"];
|
||||
|
||||
assert_eq!(second_level, expect_second_level);
|
||||
assert_eq!(third_level, expect_third_level);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fourth_level_transition() {
|
||||
let generator = Generator::from_existing(Path::from_vec(vec![2, 999, 998]).unwrap());
|
||||
|
||||
let third_level = generator.next().to_strings();
|
||||
let fourth_level = generator.next().to_strings();
|
||||
|
||||
let expect_third_level = vec!["002", "999", "999"];
|
||||
let expect_fourth_level = vec!["003", "000", "000", "000"];
|
||||
|
||||
assert_eq!(third_level, expect_third_level);
|
||||
assert_eq!(fourth_level, expect_fourth_level);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fifth_level_transition() {
|
||||
let generator = Generator::from_existing(Path::from_vec(vec![3, 999, 999, 998]).unwrap());
|
||||
|
||||
let fourth_level = generator.next().to_strings();
|
||||
let fifth_level = generator.next().to_strings();
|
||||
|
||||
let expect_fourth_level = vec!["003", "999", "999", "999"];
|
||||
let expect_fifth_level = vec!["004", "000", "000", "000", "000"];
|
||||
|
||||
assert_eq!(fourth_level, expect_fourth_level);
|
||||
assert_eq!(fifth_level, expect_fifth_level);
|
||||
}
|
Loading…
Reference in a new issue