mirror of
https://github.com/MrBeanCpp/MIT.git
synced 2026-05-08 06:22:11 +08:00
为Blob增加压缩支持,IO优化放置到Store中
This commit is contained in:
@@ -16,4 +16,6 @@ colored = "2.1.0"
|
||||
rand = "0.8.5"
|
||||
color-backtrace = "0.6.1"
|
||||
once_cell = "1.19.0"
|
||||
backtrace = "0.3.69"
|
||||
backtrace = "0.3.69"
|
||||
flate2 = "1.0.28"
|
||||
base64 = "0.21.5"
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
use base64::Engine;
|
||||
use flate2::{read::GzDecoder, write::GzEncoder, Compression};
|
||||
use std::io::{Read, Write};
|
||||
|
||||
use crate::{
|
||||
models::Hash,
|
||||
utils::{store, util},
|
||||
@@ -17,28 +21,53 @@ impl Blob {
|
||||
/// 从源文件新建blob对象,并直接保存到/objects/中
|
||||
pub fn new(file: &Path) -> Blob {
|
||||
let data = fs::read_to_string(file).expect(format!("无法读取文件:{:?}", file).as_str());
|
||||
let hash = util::calc_hash(&data);
|
||||
let blob = Blob { hash, data };
|
||||
let mut blob = Blob { hash: "".to_string(), data };
|
||||
blob.save();
|
||||
blob
|
||||
}
|
||||
|
||||
pub fn load(hash: &String) -> Blob {
|
||||
let s = store::Store::new();
|
||||
let data = s.load(hash);
|
||||
let encoded = s.load(hash);
|
||||
let compressed_data = base64::engine::general_purpose::STANDARD_NO_PAD.decode(&encoded).unwrap();
|
||||
let mut decompress_decoder = GzDecoder::new(&compressed_data[..]);
|
||||
let mut data = String::new();
|
||||
decompress_decoder.read_to_string(&mut data).unwrap();
|
||||
Blob { hash: hash.clone(), data }
|
||||
}
|
||||
|
||||
/// 写入文件;优化:文件已存在时不做操作
|
||||
pub fn save(&self) {
|
||||
/// 写入文件
|
||||
pub fn save(&mut self) -> Hash {
|
||||
let s = store::Store::new();
|
||||
if !s.contains(&self.hash) {
|
||||
let hash = s.save(&self.data);
|
||||
assert_eq!(hash, self.hash);
|
||||
}
|
||||
let mut cmopress_encoder = GzEncoder::new(Vec::new(), Compression::default());
|
||||
cmopress_encoder.write_all(self.data.as_bytes()).unwrap();
|
||||
let compressed_data = cmopress_encoder.finish().unwrap();
|
||||
let encoded_data = base64::engine::general_purpose::STANDARD_NO_PAD.encode(&compressed_data);
|
||||
let hash: String = s.save(&encoded_data);
|
||||
self.hash = hash;
|
||||
self.hash.clone()
|
||||
}
|
||||
|
||||
pub fn get_hash(&self) -> String {
|
||||
self.hash.clone()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::utils::test_util;
|
||||
|
||||
#[test]
|
||||
fn test_save_and_load() {
|
||||
test_util::setup_test_with_clean_mit();
|
||||
let test_data = "hello world";
|
||||
test_util::ensure_test_file(&PathBuf::from("a.txt"), Some(test_data));
|
||||
let blob = super::Blob::new(&PathBuf::from("a.txt"));
|
||||
|
||||
let blob2 = super::Blob::load(&blob.hash);
|
||||
assert_eq!(blob2.get_hash(), blob.get_hash());
|
||||
assert_eq!(blob2.data, test_data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ impl Tree {
|
||||
tree
|
||||
}
|
||||
|
||||
pub fn save(&mut self) -> String {
|
||||
pub fn save(&mut self) -> Hash {
|
||||
let s = store::Store::new();
|
||||
let tree_data = serde_json::to_string_pretty(&self).unwrap();
|
||||
let hash = s.save(&tree_data);
|
||||
@@ -107,28 +107,6 @@ impl Tree {
|
||||
hash
|
||||
}
|
||||
|
||||
/**递归获取Tree对应的所有文件 */
|
||||
pub fn get_recursive_file_entries(&self) -> Vec<PathBuf> {
|
||||
let mut files = Vec::new();
|
||||
for entry in self.entries.iter() {
|
||||
if entry.filemode.0 == "blob" {
|
||||
files.push(PathBuf::from(entry.name.clone()));
|
||||
} else {
|
||||
let sub_tree = Tree::load(&entry.object_hash);
|
||||
let sub_files = sub_tree.get_recursive_file_entries();
|
||||
|
||||
files.append(
|
||||
sub_files
|
||||
.iter()
|
||||
.map(|file| PathBuf::from(entry.name.clone()).join(file))
|
||||
.collect::<Vec<PathBuf>>()
|
||||
.as_mut(),
|
||||
);
|
||||
}
|
||||
}
|
||||
files
|
||||
}
|
||||
|
||||
///注:相对路径(to workdir)
|
||||
pub fn get_recursive_blobs(&self) -> Vec<(PathBuf, Hash)> {
|
||||
//TODO 返回HashMap
|
||||
@@ -159,7 +137,7 @@ mod test {
|
||||
|
||||
use crate::{
|
||||
models::*,
|
||||
utils::{util, test_util},
|
||||
utils::test_util,
|
||||
};
|
||||
|
||||
#[test]
|
||||
@@ -198,34 +176,6 @@ mod test {
|
||||
assert!(tree.entries[1].name == loaded_tree.entries[1].name);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_recursive_file_entries() {
|
||||
test_util::setup_test_with_clean_mit();
|
||||
let index = Index::get_instance();
|
||||
let mut test_files = vec![PathBuf::from("b.txt"), PathBuf::from("mit_src/a.txt")];
|
||||
for test_file in test_files.clone() {
|
||||
test_util::ensure_test_file(&test_file, None);
|
||||
index.add(test_file.clone(), FileMetaData::new(&Blob::new(&test_file), &test_file));
|
||||
}
|
||||
|
||||
let tree = Tree::new(&index);
|
||||
let tree_hash = tree.get_hash();
|
||||
|
||||
let loaded_tree = Tree::load(&tree_hash);
|
||||
let mut files = loaded_tree.get_recursive_file_entries();
|
||||
files.sort();
|
||||
test_files.sort();
|
||||
assert_eq!(files.len(), test_files.len());
|
||||
assert_eq!(
|
||||
util::to_workdir_absolute_path(&files[0]).to_str().unwrap(), //TODO 罪大恶极的路径问题
|
||||
util::get_absolute_path(&test_files[0]).to_str().unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
util::to_workdir_absolute_path(&files[1]).to_str().unwrap(),
|
||||
util::get_absolute_path(&test_files[1]).to_str().unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_recursive_blobs() {
|
||||
test_util::setup_test_with_clean_mit();
|
||||
|
||||
@@ -9,6 +9,9 @@ pub struct Store {
|
||||
store_path: PathBuf,
|
||||
}
|
||||
|
||||
/**Store负责管理objects
|
||||
* 每一个object文件名与内容的hash值相同
|
||||
*/
|
||||
impl Store {
|
||||
pub fn new() -> Store {
|
||||
util::check_repo_exist();
|
||||
@@ -26,13 +29,6 @@ impl Store {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn contains(&self, hash: &String) -> bool {
|
||||
let mut path = self.store_path.clone();
|
||||
path.push("objects");
|
||||
path.push(hash);
|
||||
path.exists()
|
||||
}
|
||||
|
||||
/// 将hash对应的文件内容(主要是blob)还原到file
|
||||
pub fn restore_to_file(&self, hash: &Hash, file: &PathBuf) {
|
||||
let content = self.load(hash);
|
||||
@@ -69,6 +65,7 @@ impl Store {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn save(&self, content: &String) -> String {
|
||||
/* 保存文件内容 */
|
||||
let hash = util::calc_hash(content);
|
||||
@@ -76,6 +73,10 @@ impl Store {
|
||||
path.push("objects");
|
||||
path.push(&hash);
|
||||
// println!("Saved to: [{}]", path.display());
|
||||
if path.exists() {
|
||||
// IO优化,文件已存在,不再写入
|
||||
return hash;
|
||||
}
|
||||
match std::fs::write(path, content) {
|
||||
Ok(_) => hash,
|
||||
Err(_) => panic!("储存库疑似损坏,无法写入文件"),
|
||||
|
||||
Reference in New Issue
Block a user