From 8e8035add69aed5920587038e099fd816ee099f0 Mon Sep 17 00:00:00 2001 From: Quanyi Ma Date: Sun, 28 Sep 2025 18:06:56 +0800 Subject: [PATCH] Fix all test cases, add files for test cases in LFS Signed-off-by: Quanyi Ma --- .gitattributes | 3 + .gitignore | 1 + src/delta/encode/mod.rs | 9 +- src/delta/mod.rs | 6 +- src/hash.rs | 2 +- src/internal/index.rs | 25 +- src/internal/object/signature.rs | 1 - src/internal/pack/cache_object.rs | 9 +- src/internal/pack/decode.rs | 40 +- src/internal/pack/encode.rs | 13 +- src/internal/pack/mod.rs | 17 +- src/lib.rs | 91 +- src/zstdelta/mod.rs | 200 +- src/zstdelta/zstdelta.rs | 205 - tests/.gitignore | 7 + tests/README.md | 0 tests/data/index/index-2 | Bin 0 -> 190 bytes tests/data/index/index-760 | Bin 0 -> 112261 bytes .../4b/00093bee9b3ef5afc5f8e3645dc39cfa2f49aa | Bin 0 -> 182 bytes .../85/4aac1e94777f3ffc8722b69f087d1244587ab7 | Bin 0 -> 646 bytes .../8a/b686eafeb1f44702738c8b0f24f2567c36da6d | Bin 0 -> 30 bytes .../c5/170dd0aae2dc2a9142add9bb24597d326714d7 | Bin 0 -> 160 bytes .../e7/002dbbc79a209462247302c7757a31ab16df1e | Bin 0 -> 86 bytes tests/data/objects/emessage.txt | 1 + .../f9/a1667a0dfce06819394c2aad557a04e9a13e56 | Bin 0 -> 56 bytes tests/data/objects/message.txt | 1 + ...bb573cef7d851957caceb491c073ee8e8de41.toml | 4643 +++++++++++++++++ ...87177923cd618a75da6c6db45bb89d92bd504.pack | 3 + ...e6c14760c956c173ede71cb28f33d921e232f.pack | 3 + ...df695086eea6253a237cb5ac44af1629e7ced.pack | 3 + ...bb573cef7d851957caceb491c073ee8e8de41.pack | 3 + ...47638aa7cb7c39f1bd1d5011a415439b887a8.pack | 3 + .../16ecdcc8f663777896bd39ca025a041b7f005e | 2 + .../bee0d45f981adf7c2926a0dc04deb7f006bcc3 | 1 + tests/refs/heads/master | 1 + 35 files changed, 4933 insertions(+), 360 deletions(-) create mode 100644 .gitattributes delete mode 100644 src/zstdelta/zstdelta.rs create mode 100644 tests/.gitignore create mode 100644 tests/README.md create mode 100644 tests/data/index/index-2 create mode 100644 tests/data/index/index-760 create mode 100644 tests/data/objects/4b/00093bee9b3ef5afc5f8e3645dc39cfa2f49aa create mode 100644 tests/data/objects/85/4aac1e94777f3ffc8722b69f087d1244587ab7 create mode 100644 tests/data/objects/8a/b686eafeb1f44702738c8b0f24f2567c36da6d create mode 100644 tests/data/objects/c5/170dd0aae2dc2a9142add9bb24597d326714d7 create mode 100644 tests/data/objects/e7/002dbbc79a209462247302c7757a31ab16df1e create mode 100644 tests/data/objects/emessage.txt create mode 100644 tests/data/objects/f9/a1667a0dfce06819394c2aad557a04e9a13e56 create mode 100644 tests/data/objects/message.txt create mode 100644 tests/data/pack-f8bbb573cef7d851957caceb491c073ee8e8de41.toml create mode 100644 tests/data/packs/git-2d187177923cd618a75da6c6db45bb89d92bd504.pack create mode 100644 tests/data/packs/pack-1d0e6c14760c956c173ede71cb28f33d921e232f.pack create mode 100644 tests/data/packs/pack-d50df695086eea6253a237cb5ac44af1629e7ced.pack create mode 100644 tests/data/packs/pack-f8bbb573cef7d851957caceb491c073ee8e8de41.pack create mode 100644 tests/data/packs/ref-delta-65d47638aa7cb7c39f1bd1d5011a415439b887a8.pack create mode 100644 tests/diff/16ecdcc8f663777896bd39ca025a041b7f005e create mode 100644 tests/diff/bee0d45f981adf7c2926a0dc04deb7f006bcc3 create mode 100644 tests/refs/heads/master diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..2b476ae9 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,3 @@ +tests/data/packs/* filter=lfs diff=lfs merge=lfs -text +*.pack filter=lfs diff=lfs merge=lfs -text +20de184187340e8b98da0fa14685b6a3e5c638ba2cf6b7b193b17c0ee1da38e8 filter=lfs diff=lfs merge=lfs -text diff --git a/.gitignore b/.gitignore index 1e7caa9e..a4940f1e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ Cargo.lock target/ +.cache_temp \ No newline at end of file diff --git a/src/delta/encode/mod.rs b/src/delta/encode/mod.rs index 58c9a42c..dfed1c54 100644 --- a/src/delta/encode/mod.rs +++ b/src/delta/encode/mod.rs @@ -244,11 +244,12 @@ mod tests { #[test] fn test_delta_fn() { - let mut source = PathBuf::from(env::current_dir().unwrap().parent().unwrap()); - source.push("../tests/diff/16ecdcc8f663777896bd39ca025a041b7f005e"); + let mut source = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + source.push("tests/diff/16ecdcc8f663777896bd39ca025a041b7f005e"); let old_data = read_zlib_data(&source).unwrap(); - let mut source = PathBuf::from(env::current_dir().unwrap().parent().unwrap()); - source.push("../tests/diff/bee0d45f981adf7c2926a0dc04deb7f006bcc3"); + + let mut source = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + source.push("tests/diff/bee0d45f981adf7c2926a0dc04deb7f006bcc3"); let new_data = read_zlib_data(&source).unwrap(); let d = DeltaDiff::new(&old_data, &new_data); diff --git a/src/delta/mod.rs b/src/delta/mod.rs index d1c014da..f9c6c003 100644 --- a/src/delta/mod.rs +++ b/src/delta/mod.rs @@ -157,11 +157,13 @@ mod tests { let data3 = vec![0u8; 100]; let mut data4 = vec![0u8; 100]; - for i in 0..2 { - data4[i] = 1; + for i in data4.iter_mut().take(2) { + *i = 1; } + let rate1 = heuristic_encode_rate_parallel(&data3, &data4); let rate2 = encode_rate(&data3, &data4); + println!( "Large partially matching data rate = {}, accurate rate = {}", rate1, rate2 diff --git a/src/hash.rs b/src/hash.rs index 8543077e..85e5c898 100644 --- a/src/hash.rs +++ b/src/hash.rs @@ -186,7 +186,7 @@ mod tests { #[test] fn test_signature_without_delta() { - let mut source = PathBuf::from(env::current_dir().unwrap().parent().unwrap()); + let mut source = PathBuf::from(env!("CARGO_MANIFEST_DIR")); source.push("tests/data/packs/pack-1d0e6c14760c956c173ede71cb28f33d921e232f.pack"); let f = std::fs::File::open(source).unwrap(); diff --git a/src/internal/index.rs b/src/internal/index.rs index 9cf6c8fc..00b9c2e5 100644 --- a/src/internal/index.rs +++ b/src/internal/index.rs @@ -1,5 +1,3 @@ -use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; -use sha1::{Digest, Sha1}; use std::collections::BTreeMap; use std::fmt::{Display, Formatter}; use std::fs::{self, File}; @@ -10,6 +8,9 @@ use std::os::unix::fs::MetadataExt; use std::path::{Path, PathBuf}; use std::time::{SystemTime, UNIX_EPOCH}; +use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; +use sha1::{Digest, Sha1}; + use crate::errors::GitError; use crate::hash::SHA1; use crate::internal::pack::wrapper::Wrapper; @@ -554,14 +555,20 @@ mod tests { #[test] fn test_check_header() { - let file = File::open("../tests/data/index/index-2").unwrap(); + let mut source = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + source.push("tests/data/index/index-2"); + + let file = File::open(source).unwrap(); let entries = Index::check_header(&mut BufReader::new(file)).unwrap(); assert_eq!(entries, 2); } #[test] fn test_index() { - let index = Index::from_file("../tests/data/index/index-760").unwrap(); + let mut source = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + source.push("tests/data/index/index-760"); + + let index = Index::from_file(source).unwrap(); assert_eq!(index.size(), 760); for (_, entry) in index.entries.iter() { println!("{entry}"); @@ -570,7 +577,10 @@ mod tests { #[test] fn test_index_to_file() { - let index = Index::from_file("../tests/data/index/index-760").unwrap(); + let mut source = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + source.push("tests/data/index/index-760"); + + let index = Index::from_file(source).unwrap(); index.to_file("/tmp/index-760").unwrap(); let new_index = Index::from_file("/tmp/index-760").unwrap(); assert_eq!(index.size(), new_index.size()); @@ -578,7 +588,10 @@ mod tests { #[test] fn test_index_entry_create() { - let file = Path::new("Cargo.toml"); // use as a normal file + let mut source = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + source.push("Cargo.toml"); + + let file = Path::new(source.as_path()); // use as a normal file let hash = SHA1::from_bytes(&[0; 20]); let workdir = Path::new("../"); let entry = IndexEntry::new_from_file(file, hash, workdir).unwrap(); diff --git a/src/internal/object/signature.rs b/src/internal/object/signature.rs index 4089e1aa..553432cb 100644 --- a/src/internal/object/signature.rs +++ b/src/internal/object/signature.rs @@ -292,7 +292,6 @@ mod tests { /// When the test case run in the GitHub Action, the timezone is +0000, so we ignore it. #[test] - #[ignore] fn test_signature_with_time() { let sign = Signature::new( SignatureType::Author, diff --git a/src/internal/pack/cache_object.rs b/src/internal/pack/cache_object.rs index c5ec81e4..950b125c 100644 --- a/src/internal/pack/cache_object.rs +++ b/src/internal/pack/cache_object.rs @@ -342,9 +342,8 @@ mod test { use lru_mem::LruCache; use super::*; + #[test] - #[ignore = "only in single thread"] - // 只在单线程测试 fn test_heap_size_record() { let mut obj = CacheObject { info: CacheObjectInfo::BaseObject(ObjectType::Blob, SHA1::default()), @@ -356,7 +355,7 @@ mod test { assert_eq!(mem.load(Ordering::Relaxed), 0); obj.set_mem_recorder(mem.clone()); obj.record_mem_size(); - assert_eq!(mem.load(Ordering::Relaxed), 1120); + assert_eq!(mem.load(Ordering::Relaxed), obj.mem_size()); drop(obj); assert_eq!(mem.load(Ordering::Relaxed), 0); } @@ -375,8 +374,8 @@ mod test { let b = ArcWrapper::new(Arc::new(a.clone()), Arc::new(AtomicBool::new(false)), None); assert!(b.heap_size() == 1024); } + #[test] - #[ignore] fn test_cache_object_with_lru() { let mut cache = LruCache::new(2048); @@ -424,7 +423,7 @@ mod test { { // a should be ejected let r = cache.get(&hash_a.to_string()); - assert!(r.is_some()); + assert!(r.is_none()); } } diff --git a/src/internal/pack/decode.rs b/src/internal/pack/decode.rs index 82e0d76b..d43ff22b 100644 --- a/src/internal/pack/decode.rs +++ b/src/internal/pack/decode.rs @@ -706,11 +706,8 @@ mod tests { #[tokio::test] async fn test_pack_check_header() { - let res = crate::test_utils::setup_lfs_file().await; - println!("{res:?}"); - let source = res - .get("git-2d187177923cd618a75da6c6db45bb89d92bd504.pack") - .unwrap(); + let mut source = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + source.push("tests/data/packs/git-2d187177923cd618a75da6c6db45bb89d92bd504.pack"); let f = fs::File::open(source).unwrap(); let mut buf_reader = BufReader::new(f); @@ -744,7 +741,7 @@ mod tests { #[test] fn test_pack_decode_without_delta() { - let mut source = PathBuf::from(env::current_dir().unwrap().parent().unwrap()); + let mut source = PathBuf::from(env!("CARGO_MANIFEST_DIR")); source.push("tests/data/packs/pack-1d0e6c14760c956c173ede71cb28f33d921e232f.pack"); let tmp = PathBuf::from("/tmp/.cache_temp"); @@ -760,7 +757,7 @@ mod tests { fn test_pack_decode_with_ref_delta() { init_logger(); - let mut source = PathBuf::from(env::current_dir().unwrap().parent().unwrap()); + let mut source = PathBuf::from(env!("CARGO_MANIFEST_DIR")); source.push("tests/data/packs/ref-delta-65d47638aa7cb7c39f1bd1d5011a415439b887a8.pack"); let tmp = PathBuf::from("/tmp/.cache_temp"); @@ -773,7 +770,7 @@ mod tests { #[test] fn test_pack_decode_no_mem_limit() { - let mut source = PathBuf::from(env::current_dir().unwrap().parent().unwrap()); + let mut source = PathBuf::from(env!("CARGO_MANIFEST_DIR")); source.push("tests/data/packs/pack-1d0e6c14760c956c173ede71cb28f33d921e232f.pack"); let tmp = PathBuf::from("/tmp/.cache_temp"); @@ -785,13 +782,10 @@ mod tests { } #[tokio::test] - #[ignore] // Take too long time async fn test_pack_decode_with_large_file_with_delta_without_ref() { init_logger(); - let file_map = crate::test_utils::setup_lfs_file().await; - let source = file_map - .get("git-2d187177923cd618a75da6c6db45bb89d92bd504.pack") - .unwrap(); + let mut source = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + source.push("tests/data/packs/git-2d187177923cd618a75da6c6db45bb89d92bd504.pack"); let tmp = PathBuf::from("/tmp/.cache_temp"); @@ -815,10 +809,8 @@ mod tests { #[tokio::test] async fn test_decode_large_file_stream() { init_logger(); - let file_map = crate::test_utils::setup_lfs_file().await; - let source = file_map - .get("git-2d187177923cd618a75da6c6db45bb89d92bd504.pack") - .unwrap(); + let mut source = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + source.push("tests/data/packs/git-2d187177923cd618a75da6c6db45bb89d92bd504.pack"); let tmp = PathBuf::from("/tmp/.cache_temp"); let f = tokio::fs::File::open(source).await.unwrap(); @@ -850,12 +842,9 @@ mod tests { } #[tokio::test] - #[ignore] // Take too long time, duplicate with `test_decode_large_file_stream` async fn test_decode_large_file_async() { - let file_map = crate::test_utils::setup_lfs_file().await; - let source = file_map - .get("git-2d187177923cd618a75da6c6db45bb89d92bd504.pack") - .unwrap(); + let mut source = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + source.push("tests/data/packs/git-2d187177923cd618a75da6c6db45bb89d92bd504.pack"); let tmp = PathBuf::from("/tmp/.cache_temp"); let f = fs::File::open(source).unwrap(); @@ -870,7 +859,7 @@ mod tests { let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel(); let handle = p.decode_async(buffered, tx); // new thread let mut cnt = 0; - while let Ok(_entry) = rx.try_recv() { + while let Some(_entry) = rx.recv().await { cnt += 1; //use entry here } let p = handle.join().unwrap(); @@ -879,7 +868,7 @@ mod tests { #[test] fn test_pack_decode_with_delta_without_ref() { - let mut source = PathBuf::from(env::current_dir().unwrap().parent().unwrap()); + let mut source = PathBuf::from(env!("CARGO_MANIFEST_DIR")); source.push("tests/data/packs/pack-d50df695086eea6253a237cb5ac44af1629e7ced.pack"); let tmp = PathBuf::from("/tmp/.cache_temp"); @@ -890,8 +879,7 @@ mod tests { p.decode(&mut buffered, |_, _| {}).unwrap(); } - #[test] - #[ignore] // Take too long time + #[test]// Take too long time fn test_pack_decode_multi_task_with_large_file_with_delta_without_ref() { let task1 = std::thread::spawn(|| { test_pack_decode_with_large_file_with_delta_without_ref(); diff --git a/src/internal/pack/encode.rs b/src/internal/pack/encode.rs index ed8560a4..a413ea33 100644 --- a/src/internal/pack/encode.rs +++ b/src/internal/pack/encode.rs @@ -274,7 +274,6 @@ impl PackEncoder { ] .concat(); - // 按顺序发送合并后的结果 for data in all_encoded_data { self.write_all_and_update(&data).await; } @@ -582,16 +581,12 @@ mod tests { } async fn get_entries_for_test() -> Arc>> { - let mut source = PathBuf::from(env::current_dir().unwrap().parent().unwrap()); - source.push("tests/data/packs/pack-f8bbb573cef7d851957caceb491c073ee8e8de41.pack"); - // let file_map = crate::test_utils::setup_lfs_file().await; - // let source = file_map - // .get("git-2d187177923cd618a75da6c6db45bb89d92bd504.pack") - // .unwrap(); - // decode pack file to get entries + let source = PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("tests/data/packs/pack-f8bbb573cef7d851957caceb491c073ee8e8de41.pack"); + let mut p = Pack::new(None, None, Some(PathBuf::from("/tmp/.cache_temp")), true); - let f = std::fs::File::open(source).unwrap(); + let f = std::fs::File::open(&source).unwrap(); tracing::info!("pack file size: {}", f.metadata().unwrap().len()); let mut reader = std::io::BufReader::new(f); let entries = Arc::new(Mutex::new(Vec::new())); diff --git a/src/internal/pack/mod.rs b/src/internal/pack/mod.rs index 604369cf..3307f838 100644 --- a/src/internal/pack/mod.rs +++ b/src/internal/pack/mod.rs @@ -30,7 +30,7 @@ pub struct Pack { pub waitlist: Arc, pub caches: Arc, pub mem_limit: Option, - pub cache_objs_mem: Arc, // the memory size of CacheObjects in this Pack + pub cache_objs_mem: Arc, pub clean_tmp: bool, } @@ -38,6 +38,13 @@ pub struct Pack { mod tests { use tracing_subscriber::util::SubscriberInitExt; + /// CAUTION: This two is same + /// 1. + /// tracing_subscriber::fmt().init(); + /// + /// 2. + /// env::set_var("RUST_LOG", "debug"); // must be set if use `fmt::init()`, or no output + /// tracing_subscriber::fmt::init(); pub(crate) fn init_logger() { let _ = tracing_subscriber::fmt::Subscriber::builder() .with_target(false) @@ -46,13 +53,5 @@ mod tests { .with_max_level(tracing::Level::DEBUG) .finish() .try_init(); // avoid multi-init - - // CAUTION: This two is same - // 1. - // tracing_subscriber::fmt().init(); - // - // 2. - // env::set_var("RUST_LOG", "debug"); // must be set if use `fmt::init()`, or no output - // tracing_subscriber::fmt::init(); } } diff --git a/src/lib.rs b/src/lib.rs index 3cd441ea..8cd21eb4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,95 +1,8 @@ //! Git-Internal is a library for encoding and decoding Git Pack format files or streams. - pub mod errors; pub mod hash; pub mod internal; pub mod utils; -mod zstdelta; -mod delta; - -// #[cfg(test)] -pub mod test_utils { - use reqwest::Client; - use ring::digest::{Context, SHA256}; - use std::collections::HashMap; - use std::env; - use std::fs::File; - use std::io::copy; - use std::path::PathBuf; - use tokio::io::{AsyncReadExt, AsyncSeekExt}; - use tokio::sync::OnceCell; - - static FILES_READY: OnceCell> = OnceCell::const_new(); - - pub async fn setup_lfs_file() -> HashMap { - FILES_READY - .get_or_init(|| async { - let files_to_download = vec![( - "git-2d187177923cd618a75da6c6db45bb89d92bd504.pack", - "0d1f01ac02481427e83ba6c110c7a3a75edd4264c5af8014d12d6800699c8409", - )]; - - let mut map = HashMap::new(); - let mut base_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - base_dir.pop(); - base_dir.push("tests/data/packs"); - for (name, sha256) in files_to_download { - let file_path = base_dir.join(name); - download_lfs_file_if_not_exist(name, &file_path, sha256).await; - map.insert(name.to_string(), file_path); - } - - map - }) - .await - .clone() - } - - async fn calculate_checksum(file: &mut tokio::fs::File, checksum: &mut Context) { - file.seek(tokio::io::SeekFrom::Start(0)).await.unwrap(); - let mut buf = [0u8; 8192]; - loop { - let n = file.read(&mut buf).await.unwrap(); - if n == 0 { - break; - } - checksum.update(&buf[..n]); - } - } - - async fn download_file( - url: &str, - output_path: &PathBuf, - ) -> Result<(), Box> { - let response = Client::new().get(url).send().await?; - let mut file = File::create(output_path)?; - let content = response.bytes().await?; - let mut content = content.as_ref(); - copy(&mut content, &mut file)?; - Ok(()) - } - - async fn check_file_hash(output_path: &PathBuf, sha256: &str) -> bool { - if output_path.exists() { - let mut ring_context = Context::new(&SHA256); - let mut file = tokio::fs::File::open(output_path).await.unwrap(); - calculate_checksum(&mut file, &mut ring_context).await; - let checksum = hex::encode(ring_context.finish().as_ref()); - checksum == sha256 - } else { - false - } - } - - async fn download_lfs_file_if_not_exist(file_name: &str, output_path: &PathBuf, sha256: &str) { - let url = format!( - "https://file.gitmega.com/packs/{file_name}", - // "https://gitmono.s3.ap-southeast-2.amazonaws.com/packs/{file_name}", - // "https://gitmono.oss-cn-beijing.aliyuncs.com/{}", - ); - if !check_file_hash(output_path, sha256).await { - download_file(&url, output_path).await.unwrap(); - } - } -} +mod zstdelta; +mod delta; \ No newline at end of file diff --git a/src/zstdelta/mod.rs b/src/zstdelta/mod.rs index 96072360..23a0fce6 100644 --- a/src/zstdelta/mod.rs +++ b/src/zstdelta/mod.rs @@ -5,7 +5,201 @@ * LICENSE file in the root directory of this source tree. */ -pub mod zstdelta; +use std::cmp; +use std::ffi::CStr; +use std::io; -pub use zstdelta::apply; -pub use zstdelta::diff; +use libc::c_void; +use zstd_sys::ZSTD_CHAINLOG_MIN; +use zstd_sys::ZSTD_CONTENTSIZE_ERROR; +use zstd_sys::ZSTD_CONTENTSIZE_UNKNOWN; +use zstd_sys::ZSTD_DCtx_setMaxWindowSize; +use zstd_sys::ZSTD_HASHLOG_MIN; +use zstd_sys::ZSTD_SEARCHLOG_MIN; +use zstd_sys::ZSTD_WINDOWLOG_MIN; +use zstd_sys::ZSTD_compress_advanced; +use zstd_sys::ZSTD_compressBound; +use zstd_sys::ZSTD_compressionParameters; +use zstd_sys::ZSTD_createCCtx; +use zstd_sys::ZSTD_createDCtx; +use zstd_sys::ZSTD_decompress_usingDict; +use zstd_sys::ZSTD_findDecompressedSize; +use zstd_sys::ZSTD_frameParameters; +use zstd_sys::ZSTD_freeCCtx; +use zstd_sys::ZSTD_freeDCtx; +use zstd_sys::ZSTD_getErrorName; +use zstd_sys::ZSTD_isError; +use zstd_sys::ZSTD_parameters; +use zstd_sys::ZSTD_strategy; + +// They are complex "#define"s that are not exposed by bindgen automatically +const ZSTD_WINDOWLOG_MAX: u32 = 30; +const ZSTD_HASHLOG_MAX: u32 = 30; + +/// Return `y` so `1 << y` is greater than `x`. +/// Note: `1 << y` might be greater than `u64::MAX`. +fn log_base2(x: u64) -> u32 { + 64 - x.leading_zeros() +} + +/// Adjust a value so it is in the given range. +fn clamp(v: u32, min: u32, max: u32) -> u32 { + cmp::max(min, cmp::min(v, max)) +} + +/// Convert zstd error code to a static string. +fn explain_error(code: usize) -> &'static str { + unsafe { + // ZSTD_getErrorName returns a static string. + let name = ZSTD_getErrorName(code); + let cstr = CStr::from_ptr(name); + cstr.to_str().expect("zstd error is utf-8") + } +} + +/// Create a "zstd delta". Compress `data` using dictionary `base`. +pub fn diff(base: &[u8], data: &[u8]) -> io::Result> { + // Customized wlog, hlog to let zstd do better at delta-ing. Use "fast" strategy, which is + // good enough assuming the primary space saving is caused by "delta-ing". + let log = log_base2((data.len() + base.len() + 1) as u64); + let wlog = clamp(log, ZSTD_WINDOWLOG_MIN, ZSTD_WINDOWLOG_MAX); + let hlog = clamp(log, ZSTD_HASHLOG_MIN, ZSTD_HASHLOG_MAX); + let cparams = ZSTD_compressionParameters { + windowLog: wlog, + chainLog: ZSTD_CHAINLOG_MIN, // useless using "fast" strategy + hashLog: hlog, + searchLog: ZSTD_SEARCHLOG_MIN, // useless using "fast" strategy + minMatch: 7, // level 1 default (see ZSTD_defaultCParameters) + targetLength: 0, // enable huffman compression of literals (for "fast" strategy) + strategy: ZSTD_strategy::ZSTD_fast, + }; + let fparams = ZSTD_frameParameters { + contentSizeFlag: 1, // needed by `apply` + checksumFlag: 0, // checksum is done at another level + noDictIDFlag: 1, // dictionary is fixed, not reused + }; + let params = ZSTD_parameters { + cParams: cparams, + fParams: fparams, + }; + + unsafe { + let cctx = ZSTD_createCCtx(); + if cctx.is_null() { + return Err(io::Error::other("cannot create CCtx")); + } + + let max_outsize = ZSTD_compressBound(data.len()); + let mut buf: Vec = vec![0; max_outsize]; + + let outsize = ZSTD_compress_advanced( + cctx, + buf.as_mut_ptr() as *mut c_void, + buf.len(), + data.as_ptr() as *const c_void, + data.len(), + base.as_ptr() as *const c_void, + base.len(), + params, + ); + + ZSTD_freeCCtx(cctx); + + if ZSTD_isError(outsize) != 0 { + let msg = format!("cannot compress ({})", explain_error(outsize)); + Err(io::Error::other(msg)) + } else { + buf.set_len(outsize); + Ok(buf) + } + } +} + +/// Apply a zstd `delta` generated by `diff` to `base`. Return reconstructed `data`. +pub fn apply(base: &[u8], delta: &[u8]) -> io::Result> { + unsafe { + let dctx = ZSTD_createDCtx(); + if dctx.is_null() { + return Err(io::Error::other("cannot create DCtx")); + } + ZSTD_DCtx_setMaxWindowSize(dctx, 1 << ZSTD_WINDOWLOG_MAX); + + let size = ZSTD_findDecompressedSize(delta.as_ptr() as *const c_void, delta.len()) as usize; + if size == ZSTD_CONTENTSIZE_ERROR as usize || size == ZSTD_CONTENTSIZE_UNKNOWN as usize { + ZSTD_freeDCtx(dctx); + let msg = "cannot get decompress size"; + return Err(io::Error::other(msg)); + } + + let mut buf = vec![0u8; size]; + + let outsize = ZSTD_decompress_usingDict( + dctx, + buf.as_mut_ptr() as *mut c_void, + size, + delta.as_ptr() as *const c_void, + delta.len(), + base.as_ptr() as *const c_void, + base.len(), + ); + ZSTD_freeDCtx(dctx); + + if ZSTD_isError(outsize) != 0 { + let msg = format!("cannot decompress ({})", explain_error(outsize)); + Err(io::Error::other(msg)) + } else if outsize != size { + let msg = format!("decompress size mismatch (expected {size}, got {outsize})"); + Err(io::Error::other(msg)) + } else { + Ok(buf) + } + } +} + +#[cfg(test)] +mod tests { + use quickcheck::quickcheck; + use rand::RngCore; + use rand::SeedableRng; + use rand_chacha::ChaChaRng; + + use super::*; + + fn check_round_trip(base: &[u8], data: &[u8]) -> bool { + let delta = diff(base, data).expect("delta"); + let reconstructed = apply(base, &delta).expect("apply"); + reconstructed[..] == data[..] + } + + #[test] + fn test_round_trip_manual() { + assert!(check_round_trip(b"", b"")); + assert!(check_round_trip(b"123", b"")); + assert!(check_round_trip(b"", b"123")); + assert!(check_round_trip(b"1234567890", b"3")); + assert!(check_round_trip(b"3", b"1234567890")); + } + + #[test] + fn test_delta_efficiency() { + // 1 MB incompressible random data + let mut base = vec![0u8; 1000000]; + let mut rng = ChaChaRng::from_seed([0; 32]); + rng.fill_bytes(&mut base); + // Change a few bytes + let mut data = base.clone(); + data[0] ^= 1; + data[10000] ^= 3; + data[900000] ^= 7; + let delta = diff(&base, &data).expect("diff"); + // Should generate a small delta. + // Note: this will fail if wlog/hlog are not tweaked. + assert!(delta.len() < 200); + } + + quickcheck! { + fn test_round_trip_quickcheck(a: Vec, b: Vec) -> bool { + check_round_trip(&a, &b) + } + } +} diff --git a/src/zstdelta/zstdelta.rs b/src/zstdelta/zstdelta.rs deleted file mode 100644 index d88bd398..00000000 --- a/src/zstdelta/zstdelta.rs +++ /dev/null @@ -1,205 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -use std::cmp; -use std::ffi::CStr; -use std::io; - -use libc::c_void; -use zstd_sys::ZSTD_CHAINLOG_MIN; -use zstd_sys::ZSTD_CONTENTSIZE_ERROR; -use zstd_sys::ZSTD_CONTENTSIZE_UNKNOWN; -use zstd_sys::ZSTD_DCtx_setMaxWindowSize; -use zstd_sys::ZSTD_HASHLOG_MIN; -use zstd_sys::ZSTD_SEARCHLOG_MIN; -use zstd_sys::ZSTD_WINDOWLOG_MIN; -use zstd_sys::ZSTD_compress_advanced; -use zstd_sys::ZSTD_compressBound; -use zstd_sys::ZSTD_compressionParameters; -use zstd_sys::ZSTD_createCCtx; -use zstd_sys::ZSTD_createDCtx; -use zstd_sys::ZSTD_decompress_usingDict; -use zstd_sys::ZSTD_findDecompressedSize; -use zstd_sys::ZSTD_frameParameters; -use zstd_sys::ZSTD_freeCCtx; -use zstd_sys::ZSTD_freeDCtx; -use zstd_sys::ZSTD_getErrorName; -use zstd_sys::ZSTD_isError; -use zstd_sys::ZSTD_parameters; -use zstd_sys::ZSTD_strategy; - -// They are complex "#define"s that are not exposed by bindgen automatically -const ZSTD_WINDOWLOG_MAX: u32 = 30; -const ZSTD_HASHLOG_MAX: u32 = 30; - -/// Return `y` so `1 << y` is greater than `x`. -/// Note: `1 << y` might be greater than `u64::MAX`. -fn log_base2(x: u64) -> u32 { - 64 - x.leading_zeros() -} - -/// Adjust a value so it is in the given range. -fn clamp(v: u32, min: u32, max: u32) -> u32 { - cmp::max(min, cmp::min(v, max)) -} - -/// Convert zstd error code to a static string. -fn explain_error(code: usize) -> &'static str { - unsafe { - // ZSTD_getErrorName returns a static string. - let name = ZSTD_getErrorName(code); - let cstr = CStr::from_ptr(name); - cstr.to_str().expect("zstd error is utf-8") - } -} - -/// Create a "zstd delta". Compress `data` using dictionary `base`. -pub fn diff(base: &[u8], data: &[u8]) -> io::Result> { - // Customized wlog, hlog to let zstd do better at delta-ing. Use "fast" strategy, which is - // good enough assuming the primary space saving is caused by "delta-ing". - let log = log_base2((data.len() + base.len() + 1) as u64); - let wlog = clamp(log, ZSTD_WINDOWLOG_MIN, ZSTD_WINDOWLOG_MAX); - let hlog = clamp(log, ZSTD_HASHLOG_MIN, ZSTD_HASHLOG_MAX); - let cparams = ZSTD_compressionParameters { - windowLog: wlog, - chainLog: ZSTD_CHAINLOG_MIN, // useless using "fast" strategy - hashLog: hlog, - searchLog: ZSTD_SEARCHLOG_MIN, // useless using "fast" strategy - minMatch: 7, // level 1 default (see ZSTD_defaultCParameters) - targetLength: 0, // enable huffman compression of literals (for "fast" strategy) - strategy: ZSTD_strategy::ZSTD_fast, - }; - let fparams = ZSTD_frameParameters { - contentSizeFlag: 1, // needed by `apply` - checksumFlag: 0, // checksum is done at another level - noDictIDFlag: 1, // dictionary is fixed, not reused - }; - let params = ZSTD_parameters { - cParams: cparams, - fParams: fparams, - }; - - unsafe { - let cctx = ZSTD_createCCtx(); - if cctx.is_null() { - return Err(io::Error::other("cannot create CCtx")); - } - - let max_outsize = ZSTD_compressBound(data.len()); - let mut buf: Vec = vec![0; max_outsize]; - - let outsize = ZSTD_compress_advanced( - cctx, - buf.as_mut_ptr() as *mut c_void, - buf.len(), - data.as_ptr() as *const c_void, - data.len(), - base.as_ptr() as *const c_void, - base.len(), - params, - ); - - ZSTD_freeCCtx(cctx); - - if ZSTD_isError(outsize) != 0 { - let msg = format!("cannot compress ({})", explain_error(outsize)); - Err(io::Error::other(msg)) - } else { - buf.set_len(outsize); - Ok(buf) - } - } -} - -/// Apply a zstd `delta` generated by `diff` to `base`. Return reconstructed `data`. -pub fn apply(base: &[u8], delta: &[u8]) -> io::Result> { - unsafe { - let dctx = ZSTD_createDCtx(); - if dctx.is_null() { - return Err(io::Error::other("cannot create DCtx")); - } - ZSTD_DCtx_setMaxWindowSize(dctx, 1 << ZSTD_WINDOWLOG_MAX); - - let size = ZSTD_findDecompressedSize(delta.as_ptr() as *const c_void, delta.len()) as usize; - if size == ZSTD_CONTENTSIZE_ERROR as usize || size == ZSTD_CONTENTSIZE_UNKNOWN as usize { - ZSTD_freeDCtx(dctx); - let msg = "cannot get decompress size"; - return Err(io::Error::other(msg)); - } - - let mut buf = vec![0u8; size]; - - let outsize = ZSTD_decompress_usingDict( - dctx, - buf.as_mut_ptr() as *mut c_void, - size, - delta.as_ptr() as *const c_void, - delta.len(), - base.as_ptr() as *const c_void, - base.len(), - ); - ZSTD_freeDCtx(dctx); - - if ZSTD_isError(outsize) != 0 { - let msg = format!("cannot decompress ({})", explain_error(outsize)); - Err(io::Error::other(msg)) - } else if outsize != size { - let msg = format!("decompress size mismatch (expected {size}, got {outsize})"); - Err(io::Error::other(msg)) - } else { - Ok(buf) - } - } -} - -#[cfg(test)] -mod tests { - use quickcheck::quickcheck; - use rand::RngCore; - use rand_chacha::ChaChaRng; - use rand::SeedableRng; - - use super::*; - - fn check_round_trip(base: &[u8], data: &[u8]) -> bool { - let delta = diff(base, data).expect("delta"); - let reconstructed = apply(base, &delta).expect("apply"); - reconstructed[..] == data[..] - } - - #[test] - fn test_round_trip_manual() { - assert!(check_round_trip(b"", b"")); - assert!(check_round_trip(b"123", b"")); - assert!(check_round_trip(b"", b"123")); - assert!(check_round_trip(b"1234567890", b"3")); - assert!(check_round_trip(b"3", b"1234567890")); - } - - #[test] - fn test_delta_efficiency() { - // 1 MB incompressible random data - let mut base = vec![0u8; 1000000]; - let mut rng = ChaChaRng::from_seed([0; 32]); - rng.fill_bytes(&mut base); - // Change a few bytes - let mut data = base.clone(); - data[0] ^= 1; - data[10000] ^= 3; - data[900000] ^= 7; - let delta = diff(&base, &data).expect("diff"); - // Should generate a small delta. - // Note: this will fail if wlog/hlog are not tweaked. - assert!(delta.len() < 200); - } - - quickcheck! { - fn test_round_trip_quickcheck(a: Vec, b: Vec) -> bool { - check_round_trip(&a, &b) - } - } -} diff --git a/tests/.gitignore b/tests/.gitignore new file mode 100644 index 00000000..86c9f55b --- /dev/null +++ b/tests/.gitignore @@ -0,0 +1,7 @@ +# Ignore all files and directories in this directory +objects/* +mda/* + +# Except for the data directory and its subdirectory +!.gitignore +!objects/.gitkeep \ No newline at end of file diff --git a/tests/README.md b/tests/README.md new file mode 100644 index 00000000..e69de29b diff --git a/tests/data/index/index-2 b/tests/data/index/index-2 new file mode 100644 index 0000000000000000000000000000000000000000..103c7b958d7e48cfb5f18c40ecec39d1990ba38a GIT binary patch literal 190 zcmZ?q402{*U|<5_)W5mK{P|}-0O?xY1GayFVo=by1jYi=ESJ_4{;6xfQ&Fd1w*mny;FN!&AJEp5j3!VGq^CN{-^@3Yf zZT8=1X$%}Csl_D_qe6mQU4eSp7<3I447f6Tf6Zs=5pUZeQ0KT(zsTX_IlV{#Ikh-V literal 0 HcmV?d00001 diff --git a/tests/data/index/index-760 b/tests/data/index/index-760 new file mode 100644 index 0000000000000000000000000000000000000000..23f2847749209018fe9c15205e509e1e0fdd17da GIT binary patch literal 112261 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ª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 literal 0 HcmV?d00001 diff --git a/tests/data/objects/85/4aac1e94777f3ffc8722b69f087d1244587ab7 b/tests/data/objects/85/4aac1e94777f3ffc8722b69f087d1244587ab7 new file mode 100644 index 0000000000000000000000000000000000000000..bbeb375195d6fc7319dc3c959f8f4e9477f6c19f GIT binary patch literal 646 zcmV;10(t#-0d11WuA5i@M6>2A*rPna%u>?o%lHfi1IEU9ce#va#teq9&r4dpORY+$ zq_e6C=D0|)-@5pjq6ueXilQVYPE#pn(u{){;xaL1c$P~LBcTi)GFE~Ro3=j1QP(u( zgiT+L<%9a5Kd9e5PeZ8v?@1pks?-+xEejH83QhKpa0h694Dul?Z|R(i%R10X8^Bzmc7U^x(uRx!!o zdBRbDt)xJ1>1^~)q=Uw@@1HDC^2+<4V_$>3<|}Av_GPv&bx?w8?eAiJ^wrPLw>t(F z;z&>zD=h0iQ$)tRtkaPcht$>k>xQvFuO_&QywBI?%o0!6a{!{AYwfP8UT9&tkRff4 ztB;W{ulAt?R_BPP>Wx@4V`K%h;c&7E`{{%=&w`S*d@4fu>%a|OAVva-DHVgFYRq{TQ9g+&n>fxw@)AMx^j09mpE@m zX;^q`vDEPpfzW)+JmfHwi2StKG+$zSw{Q4W807e@P~=(8+wQ*7(RxM194@UDJm|hY z6|An8YK(?sm8g#qlb7dsF+mW^{rxt1)(W>aVmx7*=3+0;LrJ4;qpy{!sNnI`Iq_OH z3Ku7_{BlU|Nn;OvlGK3XB#VqDHrm45e2QeZUJhBu>}JS-Rr*}w7TFn14tC%J&gKb& zHr|r(n1v0q4FY+$H(CS!HE;Adu3^zNiQ6odX9yNQ6I%ZO^JuB&IUx?=0KGR6ztqq_ gX}f0*n6^ifcWH3zjkf9q4&RwEzGB literal 0 HcmV?d00001 diff --git a/tests/data/objects/8a/b686eafeb1f44702738c8b0f24f2567c36da6d b/tests/data/objects/8a/b686eafeb1f44702738c8b0f24f2567c36da6d new file mode 100644 index 0000000000000000000000000000000000000000..30fc6519aa5d5a4348f1bfcc9bf6363c7992a767 GIT binary patch literal 30 mcmb^sIn)>88BHuYr#{Dxq0UjEw~Vgv3qzXvQ2ib Oc`S#YRqO+}rb(10RZSrP literal 0 HcmV?d00001 diff --git a/tests/data/objects/e7/002dbbc79a209462247302c7757a31ab16df1e b/tests/data/objects/e7/002dbbc79a209462247302c7757a31ab16df1e new file mode 100644 index 0000000000000000000000000000000000000000..3cb2907e8443b3fe125de8ebb5386fdfda4af4d9 GIT binary patch literal 86 zcmV-c0IC0Y0V^p=O;s>AU@$Z=Ff%bx$Vkn}$=5BfgpM?o7o!-TW$_ s!fMQJ<(e1(fkIJfaS6i_AI=pG)1Ut4X|{E&*mS>wHR{q!0CbBV%)Ux0DF6Tf literal 0 HcmV?d00001 diff --git a/tests/data/objects/emessage.txt b/tests/data/objects/emessage.txt new file mode 100644 index 00000000..d9596c06 --- /dev/null +++ b/tests/data/objects/emessage.txt @@ -0,0 +1 @@ +This is a test message. \ No newline at end of file diff --git a/tests/data/objects/f9/a1667a0dfce06819394c2aad557a04e9a13e56 b/tests/data/objects/f9/a1667a0dfce06819394c2aad557a04e9a13e56 new file mode 100644 index 0000000000000000000000000000000000000000..c6473095b9b978d90493402706696f46d11199f2 GIT binary patch literal 56 zcmV-80LTA$0V^p=O;s?qWH2-^Ff%bx$Vkn}$=5BfgpM?o7o!-TW$_ O!fMQJ������x8mni"f�y$�(��c{�h�+�s �J�����Ϩb� ǩ�ҿ�e1"�d�=ܥZ�/VD��}�͌���?�u��5t�2a5�g�ީcB����X��h����;�۝��y)!���KJ \ No newline at end of file diff --git a/tests/diff/bee0d45f981adf7c2926a0dc04deb7f006bcc3 b/tests/diff/bee0d45f981adf7c2926a0dc04deb7f006bcc3 new file mode 100644 index 00000000..e4e2eda7 --- /dev/null +++ b/tests/diff/bee0d45f981adf7c2926a0dc04deb7f006bcc3 @@ -0,0 +1 @@ +xU��j�0D{�W �'�@�H �����Zb��$���wM)%�av��vc�p����托�瑈b(��o)�q�D��#Y�В�]m�6�N[P��-��-��!&������-�s��-���6�pba�N�ȆN��� 1qu�x|c�a���g�:�F�>�X�6ΦӇ���~�������\ \ No newline at end of file diff --git a/tests/refs/heads/master b/tests/refs/heads/master new file mode 100644 index 00000000..ce2fedc3 --- /dev/null +++ b/tests/refs/heads/master @@ -0,0 +1 @@ +5bb8ee25bac1014c15abc49c56d1ee0aab1050cb \ No newline at end of file