use std::path::{PathBuf, Path};
use std::usize;
use rocket::response::NamedFile;
use std::fs::Metadata;
use std::fs;
use named_in_memory_file::NamedInMemoryFile;
use cached_file::CachedFile;
use in_memory_file::InMemoryFile;
use concurrent_hashmap::ConcHashMap;
use std::collections::hash_map::RandomState;
use std::fmt::Debug;
use std::fmt;
use std::fmt::Formatter;
use in_memory_file::FileStats;
#[derive(Debug, PartialEq)]
enum CacheError {
NoMoreFilesToRemove,
NewPriorityIsNotHighEnough,
InvalidMetadata,
InvalidPath,
}
pub struct Cache {
pub(crate) size_limit: usize,
pub(crate) min_file_size: usize,
pub(crate) max_file_size: usize,
pub(crate) priority_function: fn(usize, usize) -> usize,
pub(crate) accesses_per_refresh: Option<usize>,
pub(crate) file_map: ConcHashMap<PathBuf, InMemoryFile, RandomState>,
pub(crate) access_count_map: ConcHashMap<PathBuf, usize, RandomState>,
}
impl Debug for Cache {
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
fmt.debug_map()
.entries(self.file_map.iter().map(
|(ref k, ref v)| (k.clone(), v.clone()),
))
.finish()
}
}
impl Cache {
pub fn get<P: AsRef<Path>>(&self, path: P) -> CachedFile {
trace!("{:#?}", self);
if self.contains_key(&path.as_ref().to_path_buf()) {
self.increment_access_count(&path);
self.update_stats(&path);
if let Some(accesses_per_refresh) = self.accesses_per_refresh {
match self.access_count_map.find(&path.as_ref().to_path_buf()) {
Some(a) => {
let access_count: usize = a.get().clone();
if access_count % accesses_per_refresh == 0 {
debug!( "Refreshing entry for path: {:?}", path.as_ref() );
return self.refresh(path.as_ref())
}
}
None => warn!("Cache contains entry for {:?}, but does not tract its access counts.", path.as_ref())
}
}
} else {
return self.try_insert(path);
}
self.get_from_cache(&path)
}
pub fn refresh<P: AsRef<Path>>(&self, path: P) -> CachedFile {
let mut is_ok_to_refresh: bool = false;
if self.contains_key(&path.as_ref().to_path_buf()) {
let path_string: String = match path.as_ref().to_str() {
Some(s) => String::from(s),
None => return CachedFile::NotFound,
};
if let Ok(metadata) = fs::metadata(path_string.as_str()) {
if metadata.is_file() {
if self.file_map.find(&path.as_ref().to_path_buf()).is_some() {
is_ok_to_refresh = true;
}
}
};
}
if is_ok_to_refresh {
if let Ok(new_file) = InMemoryFile::open(path.as_ref().to_path_buf()) {
debug!("Refreshing file: {:?}", path.as_ref());
{
self.file_map.remove(&path.as_ref().to_path_buf());
self.file_map.insert(path.as_ref().to_path_buf(), new_file);
}
self.update_stats(&path);
return self.get_from_cache(path)
}
}
CachedFile::NotFound
}
pub fn remove<P: AsRef<Path>>(&self, path: P) -> bool {
if let Some(_) = self.file_map.remove(&path.as_ref().to_path_buf()) {
true
} else {
false
}
}
pub fn contains_key<P: AsRef<Path>>(&self, path: P) -> bool {
self.file_map.find(&path.as_ref().to_path_buf()).is_some()
}
pub fn alter_access_count<P: AsRef<Path>>(&self, path: P, alter_count_function: fn(&usize) -> usize) -> bool {
let new_count: usize;
{
match self.access_count_map.find(&path.as_ref().to_path_buf()) {
Some(access_count_entry) => {
new_count = alter_count_function(&access_count_entry.get());
}
None => return false,
}
}
{
self.access_count_map.insert(
path.as_ref().to_path_buf(),
new_count,
);
}
self.update_stats(&path);
return true;
}
pub fn alter_all_access_counts(&self, alter_count_function: fn(&usize) -> usize) {
let all_counts: Vec<PathBuf>;
{
all_counts = self.access_count_map
.iter()
.map(|x: (&PathBuf, &usize)| x.0.clone())
.collect();
}
for pathbuf in all_counts {
self.alter_access_count(&pathbuf, alter_count_function);
}
}
pub fn used_bytes(&self) -> usize {
self.file_map.iter().fold(
0usize,
|size, x| size + x.1.stats.size,
)
}
fn get_file_size_from_metadata<P: AsRef<Path>>(path: P) -> Result<usize, CacheError> {
let path_string: String = match path.as_ref().to_str() {
Some(s) => String::from(s),
None => return Err(CacheError::InvalidPath),
};
let metadata: Metadata = match fs::metadata(path_string.as_str()) {
Ok(m) => m,
Err(_) => return Err(CacheError::InvalidMetadata),
};
let size: usize = metadata.len() as usize;
Ok(size)
}
fn try_insert<P: AsRef<Path>>(&self, path: P) -> CachedFile {
let path: PathBuf = path.as_ref().to_path_buf();
trace!("Trying to insert file {:?}", path);
let size: usize = match Cache::get_file_size_from_metadata(&path) {
Ok(size) => size,
Err(_) => return CachedFile::NotFound
};
let required_space_for_new_file: isize = (self.used_bytes() as isize + size as isize) - self.size_limit as isize;
if size > self.max_file_size || size < self.min_file_size {
self.get_file_from_fs(&path)
} else if required_space_for_new_file < 0 && size < self.size_limit {
self.get_file_from_fs_and_add_to_cache(&path)
} else {
debug!("Trying to make room for the file");
self.increment_access_count(&path);
let new_file_priority: usize;
{
let new_file_access_count: &usize = match self.access_count_map.find(&path) {
Some(access_count) => &access_count.get(),
None => &1,
};
new_file_priority = (self.priority_function)(new_file_access_count.clone(), size);
}
match self.make_room_for_new_file(required_space_for_new_file as usize, new_file_priority) {
Ok(files_to_be_removed) => {
debug!("Made room for new file");
match InMemoryFile::open(path.as_path()) {
Ok(file) => {
for file_key in files_to_be_removed {
match self.file_map.remove(&file_key) {
Some(_) => {},
None => warn!("Likely due to concurrent mutations, a file being removed from the cache was not found because another thread removed it first.")
};
}
self.file_map.insert(path.clone(), file);
self.update_stats(&path);
let cache_file_accessor = match self.file_map.find(&path) {
Some(accessor_to_file) => accessor_to_file,
None => {
warn!("Tried to add file to cache, but it was removed before it could be added. Attempting to insert file again.");
return self.try_insert(path);
}
};
let named_in_memory_file: NamedInMemoryFile = NamedInMemoryFile::new(
path.clone(),
cache_file_accessor
);
return CachedFile::from(named_in_memory_file);
}
Err(_) => return CachedFile::NotFound
}
}
Err(_) => {
debug!("The file does not have enough priority or is too large to be accepted into the cache.");
match NamedFile::open(path.clone()) {
Ok(named_file) => CachedFile::from(named_file),
Err(_) => CachedFile::NotFound,
}
}
}
}
}
fn get_file_from_fs< P: AsRef<Path>>(&self, path: P) -> CachedFile{
debug!("File does not fit size constraints of the cache.");
match NamedFile::open(path.as_ref().to_path_buf()) {
Ok(named_file) => {
self.increment_access_count(path);
return CachedFile::from(named_file);
}
Err(_) => return CachedFile::NotFound
}
}
fn get_file_from_fs_and_add_to_cache<P: AsRef<Path>>(&self, path: P) -> CachedFile {
debug!("Cache has room for the file.");
match InMemoryFile::open(&path) {
Ok(file) => {
self.file_map.insert(path.as_ref().to_path_buf(), file);
self.increment_access_count(&path);
self.update_stats(&path);
let cache_file_accessor = match self.file_map.find(path.as_ref()) {
Some(accessor_to_file) => accessor_to_file,
None => {
warn!("Tried to add file to cache, but it was removed before it could be added. Attempting to get file again.");
return self.get_file_from_fs_and_add_to_cache(path);
}
};
let cached_file: NamedInMemoryFile = NamedInMemoryFile::new(
path.as_ref().to_path_buf(),
cache_file_accessor
);
return CachedFile::from(cached_file);
}
Err(_) => return CachedFile::NotFound,
}
}
fn make_room_for_new_file(&self, required_space: usize, new_file_priority: usize) -> Result<Vec<PathBuf>, CacheError> {
let mut possibly_freed_space: usize = 0;
let mut priority_score_to_free: usize = 0;
let mut file_paths_to_remove: Vec<PathBuf> = vec![];
let mut stats: Vec<(PathBuf, FileStats)> = self.sorted_priorities();
while possibly_freed_space < required_space {
match stats.pop() {
Some(lowest) => {
let (lowest_key, lowest_stats) = lowest;
possibly_freed_space += lowest_stats.size;
priority_score_to_free += lowest_stats.priority;
file_paths_to_remove.push(lowest_key.clone());
if priority_score_to_free > new_file_priority {
return Err(CacheError::NewPriorityIsNotHighEnough);
}
}
None => return Err(CacheError::NoMoreFilesToRemove),
};
}
Ok(file_paths_to_remove)
}
fn get_from_cache<P: AsRef<Path>>(&self, path: P) -> CachedFile {
match self.file_map.find(&path.as_ref().to_path_buf()) {
Some(in_memory_file) => {
trace!("Found file: {:?} in cache.", path.as_ref());
CachedFile::from(NamedInMemoryFile::new(
path.as_ref().to_path_buf(),
in_memory_file,
))
}
None => CachedFile::NotFound,
}
}
fn increment_access_count<P: AsRef<Path>>(&self, path: P) {
self.access_count_map.upsert(
path.as_ref().to_path_buf(),
1,
&|access_count| {
*access_count = match usize::checked_add(access_count.clone(), 1) {
Some(v) => v,
None => usize::MAX,
}
},
);
}
fn update_stats<P: AsRef<Path>>(&self, path: P) {
let access_count: usize = match self.access_count_map.find(&path.as_ref().to_path_buf()) {
Some(access_count) => access_count.get().clone(),
None => 1,
};
self.file_map.upsert(
path.as_ref().to_path_buf(),
InMemoryFile {
bytes: Vec::new(),
stats: FileStats {
size: 0,
access_count: 0,
priority: 0,
},
},
&|file_entry| {
if file_entry.stats.size == 0 {
file_entry.stats.size = Cache::get_file_size_from_metadata(&path.as_ref().to_path_buf()).unwrap_or(0);
}
file_entry.stats.access_count = access_count;
file_entry.stats.priority = (self.priority_function)(file_entry.stats.access_count, file_entry.stats.size);
},
);
}
fn sorted_priorities(&self) -> Vec<(PathBuf, FileStats)> {
let mut priorities: Vec<(PathBuf, FileStats)> = self.file_map
.iter()
.map(|x| (x.0.clone(), x.1.stats.clone()))
.collect();
priorities.sort_by(|l, r| r.1.priority.cmp(&l.1.priority));
priorities
}
}
#[cfg(test)]
mod tests {
extern crate test;
extern crate tempdir;
extern crate rand;
use super::*;
use self::tempdir::TempDir;
use self::test::Bencher;
use self::rand::rngs::StdRng;
use std::io::{Write, BufWriter};
use std::fs::File;
use rocket::response::NamedFile;
use std::io::Read;
use in_memory_file::InMemoryFile;
use concurrent_hashmap::Accessor;
use std::sync::Arc;
use std::mem;
use cache_builder::CacheBuilder;
use self::rand::FromEntropy;
use self::rand::RngCore;
const MEG1: usize = 1024 * 1024;
const MEG2: usize = MEG1 * 2;
const MEG5: usize = MEG1 * 5;
const MEG10: usize = MEG1 * 10;
const DIR_TEST: &'static str = "test1";
const FILE_MEG1: &'static str = "meg1.txt";
const FILE_MEG2: &'static str = "meg2.txt";
const FILE_MEG5: &'static str = "meg5.txt";
const FILE_MEG10: &'static str = "meg10.txt";
fn create_test_file(temp_dir: &TempDir, size: usize, name: &str) -> PathBuf {
let path = temp_dir.path().join(name);
let tmp_file = File::create(path.clone()).unwrap();
let mut rand_data: Vec<u8> = vec![0u8; size];
StdRng::from_entropy().fill_bytes(rand_data.as_mut());
let mut buffer = BufWriter::new(tmp_file);
buffer.write(&rand_data).unwrap();
path
}
impl<'a> CachedFile<'a> {
fn dummy_write(self) {
match self {
CachedFile::InMemory(cached_file) => unsafe {
let file: *const Accessor<'a, PathBuf, InMemoryFile> = Arc::into_raw(cached_file.file);
let mut v: Vec<u8> = Vec::new();
let _ = (*file).get().bytes.as_slice().read_to_end(&mut v).unwrap();
let _ = Arc::from_raw(file);
},
CachedFile::FileSystem(mut named_file) => {
let mut v: Vec<u8> = Vec::new();
let _ = named_file.read_to_end(&mut v).unwrap();
}
CachedFile::NotFound => {
panic!("tried to write using a non-existent file")
}
}
}
fn get_in_memory_file(self) -> NamedInMemoryFile<'a> {
match self {
CachedFile::InMemory(n) => n,
_ => panic!("tried to get cached file for named file"),
}
}
fn get_named_file(self) -> NamedFile {
match self {
CachedFile::FileSystem(n) => n,
_ => panic!("tried to get cached file for named file"),
}
}
}
#[bench]
fn cache_get_10mb(b: &mut Bencher) {
let cache: Cache = CacheBuilder::new()
.size_limit(MEG1 * 20)
.build()
.unwrap();
let temp_dir = TempDir::new(DIR_TEST).unwrap();
let path_10m = create_test_file(&temp_dir, MEG10, FILE_MEG10);
cache.get(&path_10m);
b.iter(|| {
let cached_file = cache.get(&path_10m);
cached_file.dummy_write()
});
}
#[bench]
fn cache_miss_10mb(b: &mut Bencher) {
let cache: Cache = CacheBuilder::new()
.size_limit(0)
.build()
.unwrap();
let temp_dir = TempDir::new(DIR_TEST).unwrap();
let path_10m = create_test_file(&temp_dir, MEG10, FILE_MEG10);
b.iter(|| {
let cached_file = cache.get(&path_10m);
cached_file.dummy_write()
});
}
#[bench]
fn named_file_read_10mb(b: &mut Bencher) {
let temp_dir = TempDir::new(DIR_TEST).unwrap();
let path_10m = create_test_file(&temp_dir, MEG10, FILE_MEG10);
b.iter(|| {
let named_file = CachedFile::from(NamedFile::open(&path_10m).unwrap());
named_file.dummy_write()
});
}
#[bench]
fn cache_get_1mb(b: &mut Bencher) {
let cache: Cache = CacheBuilder::new()
.size_limit(MEG1 * 20)
.build()
.unwrap();
let temp_dir = TempDir::new(DIR_TEST).unwrap();
let path_1m = create_test_file(&temp_dir, MEG1, FILE_MEG1);
cache.get(&path_1m);
b.iter(|| {
let cached_file = cache.get(&path_1m);
cached_file.dummy_write()
});
}
#[bench]
fn cache_miss_1mb(b: &mut Bencher) {
let cache: Cache = CacheBuilder::new()
.size_limit(0)
.build()
.unwrap();
let temp_dir = TempDir::new(DIR_TEST).unwrap();
let path_1m = create_test_file(&temp_dir, MEG1, FILE_MEG1);
b.iter(|| {
let cached_file = cache.get(&path_1m);
cached_file.dummy_write()
});
}
#[bench]
fn named_file_read_1mb(b: &mut Bencher) {
let temp_dir = TempDir::new(DIR_TEST).unwrap();
let path_1m = create_test_file(&temp_dir, MEG1, FILE_MEG1);
b.iter(|| {
let named_file = CachedFile::from(NamedFile::open(&path_1m).unwrap());
named_file.dummy_write()
});
}
#[bench]
fn cache_get_5mb(b: &mut Bencher) {
let cache: Cache = CacheBuilder::new()
.size_limit(MEG1 * 20)
.build()
.unwrap();
let temp_dir = TempDir::new(DIR_TEST).unwrap();
let path_5m = create_test_file(&temp_dir, MEG5, FILE_MEG5);
cache.get(&path_5m);
b.iter(|| {
let cached_file = cache.get(&path_5m);
cached_file.dummy_write()
});
}
#[bench]
fn cache_miss_5mb(b: &mut Bencher) {
let cache: Cache = CacheBuilder::new()
.size_limit(0)
.build()
.unwrap();
let temp_dir = TempDir::new(DIR_TEST).unwrap();
let path_5m = create_test_file(&temp_dir, MEG5, FILE_MEG5);
b.iter(|| {
let cached_file = cache.get(&path_5m);
cached_file.dummy_write()
});
}
#[bench]
fn named_file_read_5mb(b: &mut Bencher) {
let temp_dir = TempDir::new(DIR_TEST).unwrap();
let path_5m = create_test_file(&temp_dir, MEG5, FILE_MEG5);
b.iter(|| {
let named_file = CachedFile::from(NamedFile::open(&path_5m).unwrap());
named_file.dummy_write()
});
}
#[bench]
fn cache_get_1mb_from_1000_entry_cache(b: &mut Bencher) {
let temp_dir = TempDir::new(DIR_TEST).unwrap();
let path_1m = create_test_file(&temp_dir, MEG1, FILE_MEG1);
let cache: Cache = CacheBuilder::new()
.size_limit(MEG1 * 3)
.build()
.unwrap();
cache.get(&path_1m);
for i in 0..1024 {
let path = create_test_file(&temp_dir, 1024, format!("{}_1kib.txt", i).as_str());
cache.get(&path);
}
cache.alter_all_access_counts(|x| x + 1 * 100000);
assert_eq!(cache.used_bytes(), MEG1 * 2);
let named_file = CachedFile::from(NamedFile::open(&path_1m).unwrap());
b.iter(|| {
let cached_file = cache.get(&path_1m);
assert!(mem::discriminant(&cached_file) != mem::discriminant(&named_file));
cached_file.dummy_write()
});
}
#[bench]
fn cache_miss_1mb_from_1000_entry_cache(b: &mut Bencher) {
let temp_dir = TempDir::new(DIR_TEST).unwrap();
let path_1m = create_test_file(&temp_dir, MEG1, FILE_MEG1);
let cache: Cache = CacheBuilder::new()
.size_limit(MEG1)
.build()
.unwrap();
for i in 0..1024 {
let path = create_test_file(&temp_dir, 1024, format!("{}_1kib.txt", i).as_str());
cache.get(&path);
}
cache.alter_all_access_counts(|x| x + 1 * 100_000_000_000_000_000);
let named_file = CachedFile::from(NamedFile::open(&path_1m).unwrap());
b.iter(|| {
let cached_file = cache.get(&path_1m);
assert!(mem::discriminant(&cached_file) == mem::discriminant(&named_file));
cached_file.dummy_write()
});
}
#[bench]
fn cache_miss_5mb_from_1000_entry_cache(b: &mut Bencher) {
let temp_dir = TempDir::new(DIR_TEST).unwrap();
let path_5m = create_test_file(&temp_dir, MEG5, FILE_MEG1);
let cache: Cache = CacheBuilder::new()
.size_limit(MEG5)
.build()
.unwrap();
for i in 0..1024 {
let path = create_test_file(&temp_dir, 1024 * 5, format!("{}_5kib.txt", i).as_str());
cache.get(&path);
}
cache.alter_all_access_counts(|x| x + 1 * 100_000_000_000_000_000);
let named_file = CachedFile::from(NamedFile::open(&path_5m).unwrap());
b.iter(|| {
let cached_file: CachedFile = cache.get(&path_5m);
assert!(mem::discriminant(&cached_file) == mem::discriminant(&named_file));
cached_file.dummy_write()
});
}
#[bench]
fn in_memory_file_read_10mb(b: &mut Bencher) {
let temp_dir = TempDir::new(DIR_TEST).unwrap();
let path_10m = create_test_file(&temp_dir, MEG10, FILE_MEG10);
b.iter(|| {
let in_memory_file = Arc::new(InMemoryFile::open(path_10m.clone()).unwrap());
let file: *const InMemoryFile = Arc::into_raw(in_memory_file);
unsafe {
let _ = (*file).bytes.clone();
let _ = Arc::from_raw(file);
}
});
}
#[test]
fn file_exceeds_size_limit() {
let cache: Cache = CacheBuilder::new()
.size_limit(MEG1 * 8)
.build()
.unwrap();
let temp_dir = TempDir::new(DIR_TEST).unwrap();
let path_10m = create_test_file(&temp_dir, MEG10, FILE_MEG10);
let named_file = NamedFile::open(path_10m.clone()).unwrap();
assert_eq!(cache.try_insert(path_10m), CachedFile::from(named_file));
}
#[test]
fn file_replaces_other_file() {
let temp_dir = TempDir::new(DIR_TEST).unwrap();
let path_1m = create_test_file(&temp_dir, MEG1, FILE_MEG1);
let path_5m = create_test_file(&temp_dir, MEG5, FILE_MEG5);
let named_file_1m = NamedFile::open(path_1m.clone()).unwrap();
let named_file_1m_2 = NamedFile::open(path_1m.clone()).unwrap();
let mut imf_5m = InMemoryFile::open(path_5m.clone()).unwrap();
let mut imf_1m = InMemoryFile::open(path_1m.clone()).unwrap();
imf_5m.stats.access_count = 1;
imf_5m.stats.priority = 2289;
let cache: Cache = CacheBuilder::new()
.size_limit(5500000)
.build()
.unwrap();
println!("0:\n{:#?}", cache);
assert_eq!(
cache
.try_insert(path_5m.clone())
.get_in_memory_file()
.file
.as_ref()
.get(),
&imf_5m
);
println!("1:\n{:#?}", cache);
assert_eq!(
cache.try_insert(path_1m.clone()),
CachedFile::from(named_file_1m)
);
println!("2:\n{:#?}", cache);
assert_eq!(
cache.try_insert(path_1m.clone()),
CachedFile::from(named_file_1m_2)
);
println!("3:\n{:#?}", cache);
imf_1m.stats.access_count = 3;
imf_1m.stats.priority = 3072;
assert_eq!(
cache
.try_insert(path_1m.clone())
.get_in_memory_file()
.file
.as_ref()
.get(),
&imf_1m
);
println!("4:\n{:#?}", cache);
}
#[test]
fn new_file_replaces_lowest_priority_file() {
let temp_dir = TempDir::new(DIR_TEST).unwrap();
let path_1m = create_test_file(&temp_dir, MEG1, FILE_MEG1);
let path_2m = create_test_file(&temp_dir, MEG2, FILE_MEG2);
let path_5m = create_test_file(&temp_dir, MEG5, FILE_MEG5);
#[allow(unused_variables)]
let named_file_1m = NamedFile::open(path_1m.clone()).unwrap();
let cache: Cache = CacheBuilder::new()
.size_limit(MEG1 * 7 + 2000)
.build()
.unwrap();
println!("1:\n{:#?}", cache);
let mut imf_5m: InMemoryFile = InMemoryFile::open(path_5m.clone()).unwrap();
imf_5m.stats.priority = 2289;
imf_5m.stats.access_count = 1;
assert_eq!(
cache.get(&path_5m)
.get_in_memory_file()
.file
.as_ref()
.get(),
&imf_5m
);
println!("2:\n{:#?}", cache);
let mut imf_2m: InMemoryFile = InMemoryFile::open(path_2m.clone()).unwrap();
imf_2m.stats.priority = 1448;
imf_2m.stats.access_count = 1;
assert_eq!(
cache.get(&path_2m)
.get_in_memory_file()
.file
.as_ref()
.get(),
&imf_2m
);
println!("3:\n{:#?}", cache);
let mut named_1m = NamedFile::open(path_1m.clone()).unwrap();
let mut v: Vec<u8> = Vec::new();
let _ = cache
.get(&path_1m)
.get_named_file()
.read_to_end(&mut v)
.unwrap();
let mut file_vec: Vec<u8> = Vec::new();
let _ = named_1m.read_to_end(&mut file_vec);
assert_eq!(
v,
file_vec
);
println!("4:\n{:#?}", cache);
let mut imf_1m: InMemoryFile = InMemoryFile::open(path_1m.clone()).unwrap();
imf_1m.stats.priority = 2048;
imf_1m.stats.access_count = 1;
assert_eq!(
cache.get(&path_1m)
.get_in_memory_file()
.file
.as_ref()
.get()
.bytes,
imf_1m.bytes
);
println!("5:\n{:#?}", cache);
if let CachedFile::NotFound = cache.get_from_cache(&path_1m) {
panic!("Expected 1m file to be in the cache");
}
if let CachedFile::NotFound = cache.get_from_cache(&path_5m) {
panic!("Expected 5m file to be in the cache");
}
if let CachedFile::InMemory(_) = cache.get_from_cache(&path_2m) {
panic!("Expected 2m file to not be in the cache");
}
drop(cache);
}
#[test]
fn remove_file() {
let cache: Cache = CacheBuilder::new()
.size_limit(MEG1 * 10)
.build()
.unwrap();
let temp_dir = TempDir::new(DIR_TEST).unwrap();
let path_5m = create_test_file(&temp_dir, MEG5, FILE_MEG5);
let mut imf: InMemoryFile = InMemoryFile::open(path_5m.clone()).unwrap();
imf.stats.priority = 2289;
imf.stats.access_count = 1;
assert_eq!(
cache
.get(&path_5m)
.get_in_memory_file()
.file
.as_ref()
.get(),
&imf
);
cache.remove(&path_5m);
assert_eq!(cache.contains_key(&path_5m.clone()), false);
}
#[test]
fn refresh_file() {
let cache: Cache = CacheBuilder::new()
.size_limit(MEG1 * 10)
.build()
.unwrap();
let temp_dir = TempDir::new(DIR_TEST).unwrap();
let path_5m = create_test_file(&temp_dir, MEG5, FILE_MEG5);
assert_eq!(
match cache.get(&path_5m) {
CachedFile::InMemory(c) => c.file.get().stats.size,
CachedFile::FileSystem(_) => unreachable!(),
CachedFile::NotFound => unreachable!()
},
MEG5
);
let path_of_file_with_10mb_but_path_name_5m = create_test_file(&temp_dir, MEG10, FILE_MEG5);
cache.refresh(&path_5m);
assert_eq!(
match cache.get(&path_of_file_with_10mb_but_path_name_5m) {
CachedFile::InMemory(c) => c.file.get().stats.size,
CachedFile::FileSystem(_) => unreachable!(),
CachedFile::NotFound => unreachable!()
},
MEG10
);
drop(cache);
}
}