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.

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