Generate directories for better lookup times

This commit is contained in:
Aode (lion) 2021-10-18 17:42:41 -05:00
commit eb89155365
4 changed files with 352 additions and 0 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
/target
Cargo.lock

9
Cargo.toml Normal file
View 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
View 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
View 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);
}