use std::io::Write as _;
use zip::write::{SimpleFileOptions, ZipWriter};
use zip::CompressionMethod;
pub const CHUNK_SIZE: usize = 512;
fn index_entry(offset: u64, length: u32) -> [u8; 12] {
let mut e = [0u8; 12];
e[0..8].copy_from_slice(&offset.to_le_bytes());
e[8..12].copy_from_slice(&length.to_le_bytes());
e
}
const STREAM_ARN: &str = "aff4://issen-test-stream";
const MAP_ARN: &str = "aff4://issen-test-map";
const IMAGE_STREAM_ARN: &str = "aff4://issen-test-image-stream";
const MAP_ZIP_BASE: &str = "issen-test-map";
const IMAGE_ZIP_BASE: &str = "issen-test-image-stream";
const ZIP_BASE: &str = "issen-test-stream";
pub fn test_aff4_with_geometry(chunk_size: u64, chunks_per_segment: u64) -> Vec<u8> {
let turtle = format!(
"@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .\n\
@prefix aff4: <http://aff4.org/Schema#> .\n\
<{STREAM_ARN}> rdf:type aff4:ImageStream ; \
aff4:size 512 ; \
aff4:chunkSize {chunk_size} ; \
aff4:chunksInSegment {chunks_per_segment} ; \
aff4:compressionMethod aff4:NullCompressor .\n"
);
let cursor = std::io::Cursor::new(Vec::<u8>::new());
let mut zw = ZipWriter::new(cursor);
let opts = SimpleFileOptions::default().compression_method(CompressionMethod::Stored);
zw.start_file("information.turtle", opts)
.expect("start turtle");
zw.write_all(turtle.as_bytes()).expect("write turtle");
zw.start_file(format!("{ZIP_BASE}/00000000").as_str(), opts)
.expect("start bevy");
zw.write_all(&[0u8; 512]).expect("write bevy");
zw.start_file(format!("{ZIP_BASE}/00000000.index").as_str(), opts)
.expect("start index");
zw.write_all(&index_entry(0, 512)).expect("write index");
zw.finish().expect("finish zip").into_inner()
}
pub fn test_aff4(data: &[u8]) -> Vec<u8> {
let mut chunk = vec![0u8; CHUNK_SIZE];
let n = data.len().min(CHUNK_SIZE);
chunk[..n].copy_from_slice(&data[..n]);
let turtle = format!(
"@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .\n\
@prefix aff4: <http://aff4.org/Schema#> .\n\
<{STREAM_ARN}> rdf:type aff4:ImageStream ; \
aff4:size {CHUNK_SIZE} ; \
aff4:chunkSize {CHUNK_SIZE} ; \
aff4:chunksInSegment 1 ; \
aff4:compressionMethod aff4:NullCompressor .\n"
);
let cursor = std::io::Cursor::new(Vec::<u8>::new());
let mut zw = ZipWriter::new(cursor);
let opts = SimpleFileOptions::default().compression_method(CompressionMethod::Stored);
zw.start_file("information.turtle", opts)
.expect("start turtle");
zw.write_all(turtle.as_bytes()).expect("write turtle");
let bevy_name = format!("{ZIP_BASE}/00000000");
zw.start_file(bevy_name.as_str(), opts).expect("start bevy");
zw.write_all(&chunk).expect("write bevy");
let index_name = format!("{ZIP_BASE}/00000000.index");
zw.start_file(index_name.as_str(), opts)
.expect("start index");
zw.write_all(&index_entry(0, CHUNK_SIZE as u32))
.expect("write index");
zw.finish().expect("finish zip").into_inner()
}
#[cfg(test)]
pub fn test_aff4_lz4(data: &[u8]) -> Vec<u8> {
let mut chunk = vec![0u8; CHUNK_SIZE];
let n = data.len().min(CHUNK_SIZE);
chunk[..n].copy_from_slice(&data[..n]);
let mut compressed = Vec::new();
{
use std::io::Write as _;
let mut enc = lz4_flex::frame::FrameEncoder::new(&mut compressed);
enc.write_all(&chunk).expect("lz4 compress");
enc.finish().expect("lz4 finish");
}
let turtle = format!(
"@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .\n\
@prefix aff4: <http://aff4.org/Schema#> .\n\
<{STREAM_ARN}> rdf:type aff4:ImageStream ; \
aff4:size {CHUNK_SIZE} ; \
aff4:chunkSize {CHUNK_SIZE} ; \
aff4:chunksInSegment 1 ; \
aff4:compressionMethod <https://github.com/lz4/lz4> .\n"
);
let cursor = std::io::Cursor::new(Vec::<u8>::new());
let mut zw = ZipWriter::new(cursor);
let opts = SimpleFileOptions::default().compression_method(CompressionMethod::Stored);
zw.start_file("information.turtle", opts)
.expect("start turtle");
zw.write_all(turtle.as_bytes()).expect("write turtle");
let bevy_name = format!("{ZIP_BASE}/00000000");
zw.start_file(bevy_name.as_str(), opts).expect("start bevy");
zw.write_all(&compressed).expect("write bevy");
let index_name = format!("{ZIP_BASE}/00000000.index");
zw.start_file(index_name.as_str(), opts)
.expect("start index");
zw.write_all(&index_entry(0, compressed.len() as u32))
.expect("write index");
zw.finish().expect("finish zip").into_inner()
}
pub fn test_aff4_map(data: &[u8]) -> Vec<u8> {
let mut chunk = vec![0u8; CHUNK_SIZE];
let n = data.len().min(CHUNK_SIZE);
chunk[..n].copy_from_slice(&data[..n]);
let virtual_size: u64 = 1024;
let turtle = format!(
"@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .\n\
@prefix aff4: <http://aff4.org/Schema#> .\n\
<{IMAGE_STREAM_ARN}> rdf:type aff4:ImageStream ; \
aff4:size {CHUNK_SIZE} ; \
aff4:chunkSize {CHUNK_SIZE} ; \
aff4:chunksInSegment 1 ; \
aff4:compressionMethod aff4:NullCompressor .\n\
<{MAP_ARN}> rdf:type aff4:Map ; \
aff4:size {virtual_size} ; \
aff4:dependentStream <{IMAGE_STREAM_ARN}> ; \
aff4:mapGapDefaultStream aff4:Zero .\n"
);
let mut map_bin = Vec::with_capacity(28);
map_bin.extend_from_slice(&512u64.to_le_bytes()); map_bin.extend_from_slice(&512u64.to_le_bytes()); map_bin.extend_from_slice(&0u64.to_le_bytes()); map_bin.extend_from_slice(&0u32.to_le_bytes());
let idx = format!("{IMAGE_STREAM_ARN}\n");
let cursor = std::io::Cursor::new(Vec::<u8>::new());
let mut zw = ZipWriter::new(cursor);
let opts = SimpleFileOptions::default().compression_method(CompressionMethod::Stored);
zw.start_file("information.turtle", opts)
.expect("start turtle");
zw.write_all(turtle.as_bytes()).expect("write turtle");
zw.start_file(format!("{IMAGE_ZIP_BASE}/00000000").as_str(), opts)
.expect("start bevy");
zw.write_all(&chunk).expect("write bevy");
zw.start_file(format!("{IMAGE_ZIP_BASE}/00000000.index").as_str(), opts)
.expect("start index");
zw.write_all(&index_entry(0, CHUNK_SIZE as u32))
.expect("write index");
zw.start_file(format!("{MAP_ZIP_BASE}/map").as_str(), opts)
.expect("start map");
zw.write_all(&map_bin).expect("write map");
zw.start_file(format!("{MAP_ZIP_BASE}/idx").as_str(), opts)
.expect("start idx");
zw.write_all(idx.as_bytes()).expect("write idx");
zw.finish().expect("finish zip").into_inner()
}
pub fn test_aff4_encrypted() -> Vec<u8> {
let turtle = format!(
"@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .\n\
@prefix aff4: <http://aff4.org/Schema#> .\n\
<{STREAM_ARN}> rdf:type aff4:EncryptedStream ; \
aff4:size 4096 ; \
aff4:chunkSize {CHUNK_SIZE} ; \
aff4:chunksInSegment 1 ; \
aff4:keyBag <{STREAM_ARN}/keybag> ; \
aff4:compressionMethod aff4:NullCompressor .\n\
<{STREAM_ARN}/keybag> rdf:type aff4:PasswordWrappedKeyBag ; \
aff4:keySizeInBytes 32 ; \
aff4:salt \"00112233445566778899aabbccddeeff\" ; \
aff4:wrappedKey \"deadbeef\" .\n"
);
let cursor = std::io::Cursor::new(Vec::<u8>::new());
let mut zw = ZipWriter::new(cursor);
let opts = SimpleFileOptions::default().compression_method(CompressionMethod::Stored);
zw.start_file("information.turtle", opts)
.expect("start turtle");
zw.write_all(turtle.as_bytes()).expect("write turtle");
zw.finish().expect("finish zip").into_inner()
}
pub fn test_aff4_logical(segment: &str, content: &[u8], md5_hex: &str) -> Vec<u8> {
let vol = "aff4://issen-test-logical-volume";
let turtle = format!(
"@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .\n\
@prefix aff4: <http://aff4.org/Schema#> .\n\
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .\n\
<{vol}/{segment}> rdf:type aff4:FileImage , aff4:Image , aff4:zip_segment ; \
aff4:originalFileName \"./{segment}\"^^xsd:string ; \
aff4:size {} ; \
aff4:lastWritten \"2018-09-17T13:42:20+10:00\"^^xsd:datetime ; \
aff4:hash \"{md5_hex}\"^^aff4:MD5 ; \
aff4:stored <{vol}> .\n",
content.len()
);
let cursor = std::io::Cursor::new(Vec::<u8>::new());
let mut zw = ZipWriter::new(cursor);
let opts = SimpleFileOptions::default().compression_method(CompressionMethod::Stored);
zw.start_file("information.turtle", opts)
.expect("start turtle");
zw.write_all(turtle.as_bytes()).expect("write turtle");
zw.start_file(segment, opts).expect("start segment");
zw.write_all(content).expect("write segment");
zw.start_file("version.txt", opts).expect("start version");
zw.write_all(b"major=1\nminor=1\ntool=issen-test\n")
.expect("write version");
zw.finish().expect("finish zip").into_inner()
}