The code below does not compile but it demonstrates the general idea of the question. There are many borrowing/lifetime issues that appear (due to capturing/moves etc.) even though if all functions were inlined, the issues would not apply. I assume fn
cannot be used inside with the goal to capture?
Should I instead group the state in a struct and write an impl for it?
async fn verify_torrent(torrent: &Torrent, root_dir: &str) -> Vec<u64> {
async fn open_file(idx: u64) -> Option<File> {
if idx as usize >= torrent.files.len() {
return None;
}
let file_path = os::join_path(root_dir, &torrent.files[idx as usize].path);
if !os::file_exists(&file_path) {
return None;
}
Some(io::open(&file_path, fmRead))
}
let mut file_idx = 0;
let mut file = open_file(file_idx).await;
let mut piece_idx = 0;
let mut piece_buffer = vec![0; torrent.piece_length as usize];
let mut read_bytes_count = 0;
async fn next_file(file: &mut Option<File>) {
if let Some(f) = file {
f.close();
}
file = open_file(file_idx).await;
}
fn has_next_piece() -> bool {
file_idx as usize < torrent.files.len() && piece_idx < torrent.piece_count
}
fn next_piece() {
piece_idx += 1;
read_bytes_count = 0;
}
fn is_last_piece() -> bool {
piece_idx == torrent.piece_count - 1
}
fn piece_torrent_start() -> u64 {
piece_idx * torrent.piece_length
}
fn piece_torrent_end() -> u64 {
if is_last_piece() {
torrent.total_length
} else {
piece_torrent_start() + torrent.piece_length
}
}
fn piece_length() -> u64 {
piece_torrent_end() - piece_torrent_start()
}
let file_starts = torrent.files.iter().map(|f| f.length).scan(0, |acc, x| {
*acc += x;
*acc
});
fn file_torrent_start() -> u64 {
file_starts[file_idx as usize]
}
fn file_torrent_end() -> u64 {
file_starts[(file_idx + 1) as usize]
}
fn valid_hash() -> bool {
if is_last_piece() {
piece_buffer.resize(read_bytes_count as usize, 0);
}
sha1::digest(&piece_buffer) == torrent.piece_hashes[piece_idx as usize]
}
fn file_start_pos() -> u64 {
if piece_torrent_start() > file_torrent_start() {
piece_torrent_start() - file_torrent_start()
} else {
0
}
}
fn file_end_pos() -> u64 {
std::cmp::min(piece_torrent_end(), file_torrent_end()) - file_torrent_start()
}
fn file_ends_before_piece_starts() -> bool {
file_torrent_end() <= piece_torrent_start()
}
async fn read_bytes_from_file(f: &mut File) -> u64 {
if f.get_file_pos() != file_start_pos() as i32 {
f.set_file_pos(file_start_pos() as i32);
}
let bytes_read = f.read_bytes(&mut piece_buffer, read_bytes_count as usize, (file_end_pos() - file_start_pos()) as usize).await as u64;
read_bytes_count += bytes_read;
bytes_read
}
fn not_all_read_from_file(bytes_read: u64) -> bool {
file_end_pos() - file_start_pos() != bytes_read
}
fn piece_loaded() -> bool {
read_bytes_count == piece_length()
}
fn file_exhausted(f: &File) -> bool {
f.end_of_file()
}
fn mark_missing_piece() {
result.push(piece_idx);
}
let mut result = Vec::new();
while has_next_piece() {
if file_ends_before_piece_starts() {
next_file(&mut file).await;
} else if file.is_none() || not_all_read_from_file(read_bytes_from_file(&mut file.as_mut().unwrap()).await) {
mark_missing_piece();
next_piece();
} else if piece_loaded() {
if !valid_hash() {
mark_missing_piece();
}
next_piece();
} else if file_exhausted(&file.as_ref().unwrap()) {
next_file(&mut file).await;
}
}
if let Some(f) = file {
f.close();
}
result
}