use irontide_core::{Id20, Id32};
use crate::Result;
pub trait TorrentStorage: Send + Sync {
fn write_chunk(&self, piece: u32, begin: u32, data: &[u8]) -> Result<()>;
fn read_chunk(&self, piece: u32, begin: u32, length: u32) -> Result<Vec<u8>>;
fn read_piece(&self, piece: u32) -> Result<Vec<u8>>;
fn verify_piece(&self, piece: u32, expected: &Id20) -> Result<bool> {
let data = self.read_piece(piece)?;
Ok(irontide_core::sha1(&data) == *expected)
}
fn verify_piece_v2(&self, piece: u32, expected: &Id32) -> Result<bool> {
let data = self.read_piece(piece)?;
Ok(irontide_core::sha256(&data) == *expected)
}
fn hash_block(&self, piece: u32, begin: u32, length: u32) -> Result<Id32> {
let data = self.read_chunk(piece, begin, length)?;
Ok(irontide_core::sha256(&data))
}
fn write_chunk_vectored(&self, piece: u32, begin: u32, s0: &[u8], s1: &[u8]) -> Result<()> {
if s1.is_empty() {
self.write_chunk(piece, begin, s0)
} else {
let mut combined = Vec::with_capacity(s0.len() + s1.len());
combined.extend_from_slice(s0);
combined.extend_from_slice(s1);
self.write_chunk(piece, begin, &combined)
}
}
fn filesystem_info(
&self,
) -> Option<(
&std::path::Path,
&[std::path::PathBuf],
&crate::file_map::FileMap,
)> {
None
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::memory::MemoryStorage;
use irontide_core::{Lengths, sha256};
#[test]
fn verify_piece_v2_correct_hash() {
let data = vec![0xABu8; 32768];
let lengths = Lengths::new(32768, 32768, 16384);
let storage = MemoryStorage::new(lengths);
storage.write_chunk(0, 0, &data[..16384]).unwrap();
storage.write_chunk(0, 16384, &data[16384..]).unwrap();
let expected = sha256(&data);
assert!(storage.verify_piece_v2(0, &expected).unwrap());
}
#[test]
fn verify_piece_v2_wrong_hash() {
let data = vec![0xABu8; 16384];
let lengths = Lengths::new(16384, 16384, 16384);
let storage = MemoryStorage::new(lengths);
storage.write_chunk(0, 0, &data).unwrap();
let wrong = Id32::ZERO;
assert!(!storage.verify_piece_v2(0, &wrong).unwrap());
}
#[test]
fn hash_block_returns_correct_sha256() {
let data = vec![0xCDu8; 16384];
let lengths = Lengths::new(16384, 16384, 16384);
let storage = MemoryStorage::new(lengths);
storage.write_chunk(0, 0, &data).unwrap();
let hash = storage.hash_block(0, 0, 16384).unwrap();
assert_eq!(hash, sha256(&data));
}
#[test]
fn hash_block_partial_last_block() {
let data = vec![0xEFu8; 20000];
let lengths = Lengths::new(20000, 20000, 16384);
let storage = MemoryStorage::new(lengths);
storage.write_chunk(0, 0, &data[..16384]).unwrap();
storage.write_chunk(0, 16384, &data[16384..]).unwrap();
let hash = storage.hash_block(0, 16384, 3616).unwrap();
assert_eq!(hash, sha256(&data[16384..]));
}
}