You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

401 lines
14 KiB

8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
  1. // Copyright 2016 Drew Short <drew@sothr.com>.
  2. //
  3. // Licensed under the MIT license<LICENSE-MIT or http://opensource.org/licenses/MIT>.
  4. // This file may not be copied, modified, or distributed except according to those terms.
  5. extern crate flate2;
  6. extern crate image;
  7. extern crate num;
  8. extern crate sha1;
  9. use std::default::Default;
  10. use std::fs::{create_dir_all, File, remove_dir_all};
  11. use std::io::{Error, ErrorKind, Read, Write};
  12. use std::option::Option;
  13. use std::path::Path;
  14. use std::result::Result;
  15. use std::str::FromStr;
  16. use super::rustc_serialize::json;
  17. use self::flate2::Compression;
  18. use self::flate2::read::ZlibDecoder;
  19. use self::flate2::write::ZlibEncoder;
  20. use self::image::DynamicImage;
  21. use self::sha1::Sha1;
  22. pub const DEFAULT_CACHE_DIR: &'static str = "./.hash_cache";
  23. const CACHED_IMAGE_EXT: &'static str = "png";
  24. const CACHED_MATRIX_EXT: &'static str = "dft";
  25. // Caching version information
  26. const CACHE_VERSION: u32 = 1;
  27. const CACHE_METADATA_FILE: &'static str = "cache.meta";
  28. #[derive(RustcDecodable, RustcEncodable)]
  29. struct CacheMetadata {
  30. cache_version: u32,
  31. }
  32. impl Default for CacheMetadata {
  33. fn default() -> CacheMetadata {
  34. CacheMetadata {
  35. cache_version: CACHE_VERSION,
  36. }
  37. }
  38. }
  39. impl PartialEq<CacheMetadata> for CacheMetadata {
  40. fn eq(&self, other: &CacheMetadata) -> bool {
  41. self.cache_version == other.cache_version
  42. }
  43. fn ne(&self, other: &CacheMetadata) -> bool {
  44. !self.eq(&other)
  45. }
  46. }
  47. /**
  48. * Structure to hold implementation of the cache
  49. */
  50. #[repr(C)]
  51. pub struct Cache {
  52. pub cache_dir: String,
  53. pub use_cache: bool,
  54. }
  55. impl Default for Cache {
  56. fn default() -> Cache {
  57. Cache {
  58. cache_dir: String::from(DEFAULT_CACHE_DIR),
  59. use_cache: true,
  60. }
  61. }
  62. }
  63. impl Cache {
  64. /**
  65. * Create the required directories for the cache
  66. */
  67. pub fn init(&self) -> Result<(), Error> {
  68. match create_dir_all(&self.cache_dir) {
  69. Ok(_) => {
  70. let metadata_path_str = format!("{}/{}", self.cache_dir, CACHE_METADATA_FILE);
  71. let metadata_path = Path::new(&metadata_path_str);
  72. let current_metadata: CacheMetadata = Default::default();
  73. match File::open(&metadata_path) {
  74. Ok(mut file) => {
  75. // Metadata file exists, compare them
  76. let mut loaded_metadata_string = String::new();
  77. match file.read_to_string(&mut loaded_metadata_string) {
  78. Ok(_) => {
  79. let loaded_metadata: CacheMetadata =
  80. match json::decode(&loaded_metadata_string) {
  81. Ok(data) => data,
  82. Err(_) => CacheMetadata { cache_version: 0 },
  83. };
  84. // If they match, continue
  85. if current_metadata != loaded_metadata {
  86. // If they don't wipe the cache to start new
  87. match remove_dir_all(&self.cache_dir) {
  88. Ok(_) => match create_dir_all(&self.cache_dir) {
  89. Ok(_) => (),
  90. Err(e) => println!("Error: {}", e),
  91. },
  92. Err(e) => println!("Error: {}", e),
  93. };
  94. };
  95. }
  96. Err(e) => println!("Error: {}", e),
  97. };
  98. }
  99. // Metadata file doesn't exist, do nothing assume all is well,
  100. // create new metadata file
  101. Err(_) => {}
  102. };
  103. let encoded_cache_metadata = json::encode(&current_metadata).unwrap();
  104. match File::create(&metadata_path) {
  105. Ok(mut file) => {
  106. let _ = file.write(&encoded_cache_metadata.as_bytes());
  107. Ok(())
  108. }
  109. Err(e) => Err(e),
  110. }
  111. }
  112. Err(e) => Err(e),
  113. }
  114. }
  115. /**
  116. * Clean the cache directory completely
  117. */
  118. pub fn clean(&self) -> Result<(), Error> {
  119. remove_dir_all(&self.cache_dir)
  120. }
  121. /**
  122. * Get the hash of the desired file and return it as a hex string
  123. */
  124. pub fn get_file_hash(&self, path: &Path) -> Result<String, Error> {
  125. let mut source = File::open(&path)?;
  126. let mut buf: Vec<u8> = Vec::new();
  127. source.read_to_end(&mut buf)?;
  128. let mut sha1 = Sha1::new();
  129. sha1.update(&buf);
  130. let digest = sha1.digest();
  131. // Return the hex result of the hash
  132. Ok(format!("{}", digest))
  133. }
  134. /**
  135. * Put an image buffer in the cache
  136. */
  137. pub fn put_image_in_cache(
  138. &self,
  139. path: &Path,
  140. size: u32,
  141. image: &DynamicImage,
  142. ) -> Result<bool, Error> {
  143. let hash = self.get_file_hash(&path);
  144. match hash {
  145. Ok(sha1) => {
  146. let cache_path_str = format!(
  147. "{}/image/{}x{}/{}/{}.{}",
  148. self.cache_dir,
  149. size,
  150. size,
  151. &sha1[..10],
  152. sha1,
  153. CACHED_IMAGE_EXT
  154. );
  155. let cache_dir_str =
  156. format!("{}/image/{}x{}/{}", self.cache_dir, size, size, &sha1[..10]);
  157. match create_dir_all(&cache_dir_str) {
  158. Ok(_) => {
  159. let file_path = Path::new(&cache_path_str);
  160. match File::create(file_path) {
  161. Ok(_) => {
  162. // Save the file into the cache
  163. match image.save(file_path) {
  164. Ok(_) => {}
  165. Err(e) => {
  166. println!("Error: {}", e);
  167. return Err(Error::new(ErrorKind::Other, e));
  168. }
  169. }
  170. }
  171. Err(e) => {
  172. println!("Unable to create file {:?}", file_path);
  173. return Err(e);
  174. }
  175. }
  176. }
  177. Err(e) => {
  178. println!("Unable to create directory {:?}", &cache_dir_str);
  179. return Err(e);
  180. }
  181. }
  182. }
  183. Err(e) => {
  184. println!("Error: {}", e);
  185. return Err(e);
  186. }
  187. }
  188. Ok(true)
  189. }
  190. /**
  191. * Get an image buffer out of the cache
  192. */
  193. pub fn get_image_from_cache(&self, path: &Path, size: u32) -> Option<DynamicImage> {
  194. if self.use_cache {
  195. let hash = self.get_file_hash(&path);
  196. match hash {
  197. Ok(sha1) => {
  198. // Check if the file exists in the cache
  199. let cache_path_str = format!(
  200. "{}/image/{}x{}/{}/{}.{}",
  201. self.cache_dir,
  202. size,
  203. size,
  204. &sha1[..10],
  205. sha1,
  206. CACHED_IMAGE_EXT
  207. );
  208. let cached_path = Path::new(&cache_path_str);
  209. // Try to open, if it does, then we can read the image in
  210. match File::open(&cached_path) {
  211. Ok(_) => {
  212. let image = image::open(&cached_path).unwrap();
  213. Some(image)
  214. }
  215. // Don't really care here, it just means an existing cached
  216. // file doesn't exist, or can't be read.
  217. Err(_) => None,
  218. }
  219. }
  220. Err(e) => {
  221. println!("Error: {}", e);
  222. None
  223. }
  224. }
  225. } else {
  226. None
  227. }
  228. }
  229. /**
  230. * Expects a slice of slices that represents lines in the file
  231. */
  232. pub fn put_matrix_in_cache(
  233. &self,
  234. path: &Path,
  235. size: u32,
  236. file_contents: &Vec<Vec<f64>>,
  237. ) -> Result<bool, Error> {
  238. let hash = self.get_file_hash(&path);
  239. match hash {
  240. Ok(sha1) => {
  241. let cache_path_str = format!(
  242. "{}/matrix/{}x{}/{}/{}.{}",
  243. self.cache_dir,
  244. size,
  245. size,
  246. &sha1[..10],
  247. sha1,
  248. CACHED_MATRIX_EXT
  249. );
  250. let cache_dir_str = format!(
  251. "{}/matrix/{}x{}/{}",
  252. self.cache_dir,
  253. size,
  254. size,
  255. &sha1[..10]
  256. );
  257. match create_dir_all(cache_dir_str) {
  258. Ok(_) => {
  259. let cached_path = Path::new(&cache_path_str);
  260. // Save the file into the cache
  261. match File::create(&cached_path) {
  262. Ok(mut file) => {
  263. let mut compressor =
  264. ZlibEncoder::new(Vec::new(), Compression::default());
  265. for row in file_contents {
  266. let mut row_str =
  267. row.iter().fold(String::new(), |acc, &item| {
  268. acc + &format!("{},", item)
  269. });
  270. // remove the last comma
  271. let desire_len = row_str.len() - 1;
  272. row_str.truncate(desire_len);
  273. row_str.push_str("\n");
  274. compressor.write(&row_str.into_bytes())?;
  275. }
  276. let compressed_matrix = match compressor.finish() {
  277. Ok(data) => data,
  278. Err(e) => {
  279. println!("Unable to compress matrix data: {}", e);
  280. return Err(e);
  281. }
  282. };
  283. file.write(&compressed_matrix)?;
  284. file.flush()?;
  285. }
  286. Err(e) => {
  287. return Err(e);
  288. }
  289. }
  290. }
  291. Err(e) => println!("Error: {}", e),
  292. }
  293. }
  294. Err(e) => {
  295. println!("Error: {}", e);
  296. return Err(e);
  297. }
  298. }
  299. Ok(true)
  300. }
  301. /**
  302. * Get a matrix out of the cache
  303. */
  304. pub fn get_matrix_from_cache(&self, path: &Path, size: u32) -> Option<Vec<Vec<f64>>> {
  305. if self.use_cache {
  306. let hash = self.get_file_hash(&path);
  307. match hash {
  308. Ok(sha1) => {
  309. // Check if the file exists in the cache
  310. let cache_path_str = format!(
  311. "{}/matrix/{}x{}/{}/{}.{}",
  312. self.cache_dir,
  313. size,
  314. size,
  315. &sha1[..10],
  316. sha1,
  317. CACHED_MATRIX_EXT
  318. );
  319. let cached_path = Path::new(&cache_path_str);
  320. // Try to open, if it does, then we can read the image in
  321. match File::open(&cached_path) {
  322. Ok(file) => {
  323. let mut decoder = ZlibDecoder::new(&file);
  324. let mut matrix_data_str = String::new();
  325. match decoder.read_to_string(&mut matrix_data_str) {
  326. Ok(_) => {}
  327. Err(e) => {
  328. println!("Unable to decompress matrix: {}", e);
  329. return None;
  330. }
  331. };
  332. // convert the matrix
  333. let matrix: Vec<Vec<f64>> = matrix_data_str
  334. .trim()
  335. .split("\n")
  336. .map(|line| {
  337. line.split(",").map(|f| f64::from_str(f).unwrap()).collect()
  338. })
  339. .collect();
  340. Some(matrix)
  341. }
  342. // Don't really care here, it just means an existing cached
  343. // file doesn't exist, or can't be read.
  344. Err(_) => None,
  345. }
  346. }
  347. Err(e) => {
  348. println!("Error: {}", e);
  349. None
  350. }
  351. }
  352. } else {
  353. None
  354. }
  355. }
  356. }
  357. #[cfg(test)]
  358. mod tests {
  359. use std::path::Path;
  360. use cache::Cache;
  361. #[test]
  362. fn test_get_file_hash() {
  363. let target = "test_images/sample_01_large.jpg";
  364. let target_path = Path::new(target);
  365. let cache: Cache = Default::default();
  366. let hash = cache.get_file_hash(&target_path);
  367. match hash {
  368. Ok(v) => {
  369. println!("Hash: {}", v);
  370. assert_eq!(v, String::from("4beb6f2d852b75a313863916a1803ebad13a3196"));
  371. }
  372. Err(e) => {
  373. println!("Error: {:?}", e);
  374. assert!(false);
  375. }
  376. }
  377. }
  378. }