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