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.

375 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
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 self::flate2::Compression;
  10. use self::flate2::read::ZlibDecoder;
  11. use self::flate2::write::ZlibEncoder;
  12. use self::image::DynamicImage;
  13. use self::sha1::Sha1;
  14. use std::default::Default;
  15. use std::fs::{create_dir_all, File, remove_dir_all};
  16. use std::io::{Error, ErrorKind, Read, Write};
  17. use std::option::Option;
  18. use std::path::Path;
  19. use std::result::Result;
  20. use std::str::FromStr;
  21. use super::rustc_serialize::json;
  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 { cache_version: CACHE_VERSION }
  35. }
  36. }
  37. impl PartialEq<CacheMetadata> for CacheMetadata {
  38. fn eq(&self, other: &CacheMetadata) -> bool {
  39. self.cache_version == other.cache_version
  40. }
  41. fn ne(&self, other: &CacheMetadata) -> bool {
  42. !self.eq(&other)
  43. }
  44. }
  45. /**
  46. * Structure to hold implementation of the cache
  47. */
  48. #[repr(C)]
  49. pub struct Cache<'a> {
  50. pub cache_dir: &'a str,
  51. pub use_cache: bool,
  52. }
  53. impl<'a> Default for Cache<'a> {
  54. fn default() -> Cache<'a> {
  55. Cache {
  56. cache_dir: DEFAULT_CACHE_DIR,
  57. use_cache: true,
  58. }
  59. }
  60. }
  61. impl<'a> Cache<'a> {
  62. /**
  63. * Create the required directories for the cache
  64. */
  65. pub fn init(&self) -> Result<(), Error> {
  66. match create_dir_all(self.cache_dir) {
  67. Ok(_) => {
  68. let metadata_path_str = format!("{}/{}", self.cache_dir, CACHE_METADATA_FILE);
  69. let metadata_path = Path::new(&metadata_path_str);
  70. let current_metadata: CacheMetadata = Default::default();
  71. match File::open(&metadata_path) {
  72. Ok(mut file) => {
  73. // Metadata file exists, compare them
  74. let mut loaded_metadata_string = String::new();
  75. match file.read_to_string(&mut loaded_metadata_string) {
  76. Ok(_) => {
  77. let loaded_metadata: CacheMetadata =
  78. match json::decode(&loaded_metadata_string) {
  79. Ok(data) => data,
  80. Err(_) => CacheMetadata { cache_version: 0 },
  81. };
  82. // If they match, continue
  83. if current_metadata != loaded_metadata {
  84. // If they don't wipe the cache to start new
  85. match remove_dir_all(self.cache_dir) {
  86. Ok(_) => {
  87. match create_dir_all(self.cache_dir) {
  88. Ok(_) => (),
  89. Err(e) => println!("Error: {}", e),
  90. }
  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 = try!(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(&self,
  138. path: &Path,
  139. size: u32,
  140. image: &DynamicImage)
  141. -> Result<bool, Error> {
  142. let hash = self.get_file_hash(&path);
  143. match hash {
  144. Ok(sha1) => {
  145. let cache_path_str = format!("{}/image/{}x{}/{}/{}.{}",
  146. self.cache_dir,
  147. size,
  148. size,
  149. &sha1[..10],
  150. sha1,
  151. CACHED_IMAGE_EXT);
  152. let cache_dir_str = format!("{}/image/{}x{}/{}", self.cache_dir, size, size, &sha1[..10]);
  153. // println!("Saving: {}", cache_path_str);
  154. match create_dir_all(cache_dir_str) {
  155. Ok(_) => {
  156. let file_path = Path::new(&cache_path_str);
  157. match File::create(file_path) {
  158. Ok(mut file) => {
  159. // Save the file into the cache
  160. match image.save(file_path) {
  161. Ok(_) => {}
  162. Err(e) => {
  163. println!("Error: {}", e);
  164. return Err(Error::new(ErrorKind::Other, e));
  165. }
  166. }
  167. }
  168. Err(e) => return Err(e),
  169. }
  170. }
  171. Err(e) => return Err(e),
  172. }
  173. }
  174. Err(e) => {
  175. println!("Error: {}", e);
  176. return Err(e);
  177. }
  178. }
  179. Ok(true)
  180. }
  181. /**
  182. * Get an image buffer out of the cache
  183. */
  184. pub fn get_image_from_cache(&self,
  185. path: &Path,
  186. size: u32)
  187. -> Option<DynamicImage> {
  188. if self.use_cache {
  189. let hash = self.get_file_hash(&path);
  190. match hash {
  191. Ok(sha1) => {
  192. // Check if the file exists in the cache
  193. let cache_path_str = format!("{}/image/{}x{}/{}/{}.{}",
  194. self.cache_dir,
  195. size,
  196. size,
  197. &sha1[..10],
  198. sha1,
  199. CACHED_IMAGE_EXT);
  200. let cached_path = Path::new(&cache_path_str);
  201. // Try to open, if it does, then we can read the image in
  202. match File::open(&cached_path) {
  203. Ok(_) => {
  204. let image = image::open(&cached_path).unwrap();
  205. Some(image)
  206. }
  207. // Don't really care here, it just means an existing cached
  208. // file doesn't exist, or can't be read.
  209. Err(_) => None,
  210. }
  211. }
  212. Err(e) => {
  213. println!("Error: {}", e);
  214. None
  215. }
  216. }
  217. } else {
  218. None
  219. }
  220. }
  221. /**
  222. * Expects a slice of slices that represents lines in the file
  223. */
  224. pub fn put_matrix_in_cache(&self,
  225. path: &Path,
  226. size: u32,
  227. file_contents: &Vec<Vec<f64>>)
  228. -> Result<bool, Error> {
  229. let hash = self.get_file_hash(&path);
  230. match hash {
  231. Ok(sha1) => {
  232. let cache_path_str = format!("{}/matrix/{}x{}/{}/{}.{}",
  233. self.cache_dir,
  234. size,
  235. size,
  236. &sha1[..10],
  237. sha1,
  238. CACHED_MATRIX_EXT);
  239. let cache_dir_str = format!("{}/matrix/{}x{}/{}", self.cache_dir, size, size, &sha1[..10]);
  240. match create_dir_all(cache_dir_str) {
  241. Ok(_) => {
  242. let cached_path = Path::new(&cache_path_str);
  243. // Save the file into the cache
  244. match File::create(&cached_path) {
  245. Ok(mut file) => {
  246. let mut compressor = ZlibEncoder::new(Vec::new(),
  247. Compression::default());
  248. for row in file_contents {
  249. let mut row_str =
  250. row.iter()
  251. .fold(String::new(),
  252. |acc, &item| acc + &format!("{},", item));
  253. // remove the last comma
  254. let desire_len = row_str.len() - 1;
  255. row_str.truncate(desire_len);
  256. row_str.push_str("\n");
  257. try!(compressor.write(&row_str.into_bytes()));
  258. }
  259. let compressed_matrix = match compressor.finish() {
  260. Ok(data) => data,
  261. Err(e) => {
  262. println!("Unable to compress matrix data: {}", e);
  263. return Err(e);
  264. }
  265. };
  266. try!(file.write(&compressed_matrix));
  267. try!(file.flush());
  268. }
  269. Err(e) => {
  270. return Err(e);
  271. }
  272. }
  273. }
  274. Err(e) => println!("Error: {}", e),
  275. }
  276. }
  277. Err(e) => {
  278. println!("Error: {}", e);
  279. return Err(e);
  280. }
  281. }
  282. Ok(true)
  283. }
  284. /**
  285. * Get a matrix out of the cache
  286. */
  287. pub fn get_matrix_from_cache(&self, path: &Path, size: u32) -> Option<Vec<Vec<f64>>> {
  288. if self.use_cache {
  289. let hash = self.get_file_hash(&path);
  290. match hash {
  291. Ok(sha1) => {
  292. // Check if the file exists in the cache
  293. let cache_path_str = format!("{}/matrix/{}x{}/{}/{}.{}",
  294. self.cache_dir,
  295. size,
  296. size,
  297. &sha1[..10],
  298. sha1,
  299. CACHED_MATRIX_EXT);
  300. let cached_path = Path::new(&cache_path_str);
  301. // Try to open, if it does, then we can read the image in
  302. match File::open(&cached_path) {
  303. Ok(file) => {
  304. let mut decoder = ZlibDecoder::new(&file);
  305. let mut matrix_data_str = String::new();
  306. match decoder.read_to_string(&mut matrix_data_str) {
  307. Ok(_) => {}
  308. Err(e) => {
  309. println!("Unable to decompress matrix: {}", e);
  310. return None;
  311. }
  312. };
  313. // convert the matrix
  314. let matrix: Vec<Vec<f64>> = matrix_data_str
  315. .trim()
  316. .split("\n")
  317. .map(|line| {
  318. line.split(",")
  319. .map(|f| f64::from_str(f).unwrap())
  320. .collect()
  321. })
  322. .collect();
  323. Some(matrix)
  324. }
  325. // Don't really care here, it just means an existing cached
  326. // file doesn't exist, or can't be read.
  327. Err(_) => None,
  328. }
  329. }
  330. Err(e) => {
  331. println!("Error: {}", e);
  332. None
  333. }
  334. }
  335. } else {
  336. None
  337. }
  338. }
  339. }
  340. #[test]
  341. fn test_get_file_hash() {
  342. let target = "test_images/sample_01_large.jpg";
  343. let target_path = Path::new(target);
  344. let cache: Cache = Default::default();
  345. let hash = cache.get_file_hash(&target_path);
  346. match hash {
  347. Ok(v) => {
  348. println!("Hash: {}", v);
  349. assert_eq!(v, String::from("4beb6f2d852b75a313863916a1803ebad13a3196"));
  350. }
  351. Err(e) => {
  352. println!("Error: {:?}", e);
  353. assert!(false);
  354. }
  355. }
  356. }