From 31b82cb8bb6965522e86d330d98093e359f3ba13 Mon Sep 17 00:00:00 2001 From: webdesus Date: Sun, 9 Dec 2018 19:26:11 +0300 Subject: [PATCH 1/4] Developed by change_block function for File --- src/file.rs | 59 +++++++++++++++++++++++- tests/file.rs | 123 +++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 179 insertions(+), 3 deletions(-) diff --git a/src/file.rs b/src/file.rs index a50bbc2..bd62452 100644 --- a/src/file.rs +++ b/src/file.rs @@ -1,7 +1,8 @@ use error::{Error, ErrorKind, Result}; use std; +use std::cmp; use std::fs::{remove_file, File}; -use std::io::{Read, Write}; +use std::io::{Read, Seek, SeekFrom, Write}; use std::path::Path; /// Options and flags which can be used to configure how a file will be copied or moved. @@ -383,3 +384,59 @@ where Ok(f.write_all(content.as_bytes())?) } + +/// Information block which should be changed +pub struct Block { + pub begin: u64, + pub len: u64, +} +pub trait ExtraFile { + fn change_block(&mut self, block: &Block, data: &[u8], buffer_size: u64); +} + +impl ExtraFile for File { + fn change_block(&mut self, block: &Block, data: &[u8], buffer_size: u64) { + let file_len = self.metadata().unwrap().len(); + if file_len < (block.begin + block.len) { + //TODO make as error + panic!("The selected block is out of bounds file size!"); + } + + let mut bytes_to_move = file_len - (block.begin + block.len); + let mut move_cursor = file_len; + let data_len = data.len() as u64; + let is_move_left = block.len > data_len; + if is_move_left { + move_cursor = block.begin; + } + if block.len == data_len { + bytes_to_move = 0; + } + while bytes_to_move != 0 { + let bytes_this_time: u64 = cmp::min(buffer_size, bytes_to_move); + let mut r_buffer = vec![0; bytes_this_time as usize]; + let rd_off: u64; + let wr_off: u64; + if is_move_left { + rd_off = move_cursor + block.len; + wr_off = rd_off - block.len + data_len; + move_cursor += bytes_this_time; + } else { + rd_off = move_cursor - bytes_this_time; + wr_off = rd_off - block.len + data_len; + move_cursor -= bytes_this_time; + } + self.seek(SeekFrom::Start(rd_off)).unwrap(); + self.read(&mut r_buffer[..]).unwrap(); + self.seek(SeekFrom::Start(wr_off)).unwrap(); + self.write_all(&r_buffer).unwrap(); + bytes_to_move -= bytes_this_time; + } + self.seek(SeekFrom::Start(block.begin)).unwrap(); + self.write_all(&data).unwrap(); + self.flush().unwrap(); + if is_move_left { + self.set_len(file_len - (block.len - data_len)).unwrap(); + } + } +} diff --git a/tests/file.rs b/tests/file.rs index c7fdb36..a9c6bf4 100644 --- a/tests/file.rs +++ b/tests/file.rs @@ -1,11 +1,13 @@ // use std::io::{ErrorKind, Result}; +use std::fs::OpenOptions; +use std::io::{prelude::*, Seek, SeekFrom}; use std::path::{Path, PathBuf}; -use std::thread; use std::sync::mpsc; +use std::thread; extern crate fs_extra; -use fs_extra::file::*; use fs_extra::error::*; +use fs_extra::file::*; const TEST_FOLDER: &'static str = "./tests/temp/file"; @@ -1034,3 +1036,120 @@ fn it_move_with_progress_exist_overwrite_and_skip_exist() { Err(err) => panic!(err.to_string()), } } +struct Case { + src: Vec, + block: Block, + data: Vec, + out: Vec, +} +fn get_cases() -> Vec { + vec![ + Case { + src: vec![1, 2, 3, 4, 5], + block: Block { begin: 2, len: 2 }, + data: vec![7, 7, 7, 7, 7], + out: vec![1, 2, 7, 7, 7, 7, 7, 5], + }, + Case { + src: vec![1, 2, 3, 4, 5], + block: Block { begin: 2, len: 0 }, + data: vec![7, 7, 7, 7, 7], + out: vec![1, 2, 7, 7, 7, 7, 7, 3, 4, 5], + }, + Case { + src: vec![1, 2, 3, 4, 5], + block: Block { begin: 1, len: 3 }, + data: vec![7, 7], + out: vec![1, 7, 7, 5], + }, + Case { + src: vec![1, 2, 3, 4, 5], + block: Block { begin: 1, len: 2 }, + data: vec![7], + out: vec![1, 7, 4, 5], + }, + Case { + src: vec![1, 2, 3, 4, 5], + block: Block { begin: 1, len: 2 }, + data: vec![7, 3], + out: vec![1, 7, 3, 4, 5], + }, + ] +} + +#[test] +fn it_change_block_buffer_size_1_byte() { + let cases = get_cases(); + let mut test_file = std::fs::canonicalize(PathBuf::from(TEST_FOLDER)).unwrap(); + test_file.push("it_change_block_buffer_size_1_byte"); + test_file.push("file.db"); + fs_extra::dir::create_all(&test_file.parent().unwrap(), true).unwrap(); + for case in cases { + let mut file = OpenOptions::new() + .read(true) + .write(true) + .truncate(true) + .create(true) + .open(&test_file) + .unwrap(); + file.seek(SeekFrom::Start(0)).unwrap(); + file.write_all(&case.src).unwrap(); + file.change_block(&case.block, &case.data, 1); + let mut buffer = vec![0; case.out.len()]; + file.seek(SeekFrom::Start(0)).unwrap(); + file.read(&mut buffer[..]).unwrap(); + assert_eq!(case.out, buffer); + assert_eq!(file.metadata().unwrap().len(), case.out.len() as u64); + } +} +#[test] +fn it_change_block_test_buffer_size_1_mb() { + let cases = get_cases(); + let mut test_file = std::fs::canonicalize(PathBuf::from(TEST_FOLDER)).unwrap(); + test_file.push("it_change_block_test_buffer_size_1_mb"); + test_file.push("file_1mb.db"); + fs_extra::dir::create_all(&test_file.parent().unwrap(), true).unwrap(); + for case in cases { + let mut file = OpenOptions::new() + .read(true) + .write(true) + .truncate(true) + .create(true) + .open(&test_file) + .unwrap(); + file.seek(SeekFrom::Start(0)).unwrap(); + file.write_all(&case.src).unwrap(); + file.change_block(&case.block, &case.data, 1024 * 1024); + let mut buffer = vec![0; case.out.len()]; + file.seek(SeekFrom::Start(0)).unwrap(); + file.read(&mut buffer[..]).unwrap(); + assert_eq!(case.out, buffer); + assert_eq!(file.metadata().unwrap().len(), case.out.len() as u64); + } +} + +#[test] +#[should_panic(expected = "The selected block is out of bounds file size!")] +fn it_change_block_test_out_bounds() { + let mut test_file = std::fs::canonicalize(PathBuf::from(TEST_FOLDER)).unwrap(); + test_file.push("it_change_block_test_out_bounds"); + test_file.push("file_panic.db"); + fs_extra::dir::create_all(&test_file.parent().unwrap(), true).unwrap(); + let case = Case { + src: vec![1, 2, 3, 4, 5], + block: Block { begin: 4, len: 6 }, + data: vec![7, 7, 7, 7, 7], + out: vec![1, 2, 7, 7, 7, 7, 7, 5], + }; + + let mut file = OpenOptions::new() + .read(true) + .write(true) + .truncate(true) + .create(true) + .open(test_file) + .unwrap(); + file.seek(SeekFrom::Start(0)).unwrap(); + file.write_all(&case.src).unwrap(); + file.change_block(&case.block, &case.data, 1024 * 1024); +} From 0fdcb1a89fb7d0624f5fbea9917fdece354f1141 Mon Sep 17 00:00:00 2001 From: webdesus Date: Thu, 13 Dec 2018 01:35:36 +0300 Subject: [PATCH 2/4] write change_blockcs function --- src/file.rs | 17 +++- tests/file.rs | 225 ++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 221 insertions(+), 21 deletions(-) diff --git a/src/file.rs b/src/file.rs index bd62452..e24a1da 100644 --- a/src/file.rs +++ b/src/file.rs @@ -389,13 +389,15 @@ where pub struct Block { pub begin: u64, pub len: u64, + pub data: Vec, } pub trait ExtraFile { - fn change_block(&mut self, block: &Block, data: &[u8], buffer_size: u64); + fn change_block(&mut self, block: &Block, buffer_size: u64); + fn change_blocks(&mut self, block: Vec, buffer_size: u64); } impl ExtraFile for File { - fn change_block(&mut self, block: &Block, data: &[u8], buffer_size: u64) { + fn change_block(&mut self, block: &Block, buffer_size: u64) { let file_len = self.metadata().unwrap().len(); if file_len < (block.begin + block.len) { //TODO make as error @@ -404,7 +406,7 @@ impl ExtraFile for File { let mut bytes_to_move = file_len - (block.begin + block.len); let mut move_cursor = file_len; - let data_len = data.len() as u64; + let data_len = block.data.len() as u64; let is_move_left = block.len > data_len; if is_move_left { move_cursor = block.begin; @@ -433,10 +435,17 @@ impl ExtraFile for File { bytes_to_move -= bytes_this_time; } self.seek(SeekFrom::Start(block.begin)).unwrap(); - self.write_all(&data).unwrap(); + self.write_all(&block.data).unwrap(); self.flush().unwrap(); if is_move_left { self.set_len(file_len - (block.len - data_len)).unwrap(); } } + fn change_blocks(&mut self, blocks: Vec, buffer_size: u64) { + let mut blocks = blocks; + blocks.sort_by(|a, b| b.begin.cmp(&a.begin)); + for b in blocks { + self.change_block(&b, buffer_size.clone()); + } + } } diff --git a/tests/file.rs b/tests/file.rs index a9c6bf4..dd9b2d9 100644 --- a/tests/file.rs +++ b/tests/file.rs @@ -1038,44 +1038,152 @@ fn it_move_with_progress_exist_overwrite_and_skip_exist() { } struct Case { src: Vec, - block: Block, - data: Vec, + blocks: Vec, out: Vec, } fn get_cases() -> Vec { vec![ Case { src: vec![1, 2, 3, 4, 5], - block: Block { begin: 2, len: 2 }, - data: vec![7, 7, 7, 7, 7], + blocks: vec![Block { + begin: 2, + len: 2, + data: vec![7, 7, 7, 7, 7], + }], out: vec![1, 2, 7, 7, 7, 7, 7, 5], }, Case { src: vec![1, 2, 3, 4, 5], - block: Block { begin: 2, len: 0 }, - data: vec![7, 7, 7, 7, 7], + blocks: vec![Block { + begin: 2, + len: 0, + data: vec![7, 7, 7, 7, 7], + }], out: vec![1, 2, 7, 7, 7, 7, 7, 3, 4, 5], }, Case { src: vec![1, 2, 3, 4, 5], - block: Block { begin: 1, len: 3 }, - data: vec![7, 7], + blocks: vec![Block { + begin: 1, + len: 3, + data: vec![7, 7], + }], out: vec![1, 7, 7, 5], }, Case { src: vec![1, 2, 3, 4, 5], - block: Block { begin: 1, len: 2 }, - data: vec![7], + blocks: vec![Block { + begin: 1, + len: 2, + data: vec![7], + }], out: vec![1, 7, 4, 5], }, Case { src: vec![1, 2, 3, 4, 5], - block: Block { begin: 1, len: 2 }, - data: vec![7, 3], + blocks: vec![Block { + begin: 1, + len: 2, + data: vec![7, 3], + }], out: vec![1, 7, 3, 4, 5], }, ] } +fn get_multi_cases() -> Vec { + vec![ + Case { + src: vec![1, 2, 3, 4, 5], + blocks: vec![ + Block { + begin: 2, + len: 2, + data: vec![7, 7, 7, 7, 7], + }, + Block { + begin: 4, + len: 1, + data: vec![8, 8, 8, 8, 8], + }, + ], + out: vec![1, 2, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8], + }, + Case { + src: vec![1, 2, 3, 4, 5], + blocks: vec![ + Block { + begin: 2, + len: 0, + data: vec![7, 7, 7, 7, 7], + }, + Block { + begin: 4, + len: 0, + data: vec![8, 8, 8, 8, 8], + }, + ], + out: vec![1, 2, 7, 7, 7, 7, 7, 3, 4, 8, 8, 8, 8, 8, 5], + }, + Case { + src: vec![1, 2, 3, 4, 5, 6], + blocks: vec![ + Block { + begin: 1, + len: 3, + data: vec![7, 7], + }, + Block { + begin: 4, + len: 2, + data: vec![8], + }, + ], + out: vec![1, 7, 7, 8], + }, + Case { + src: vec![1, 2, 3, 4, 5], + blocks: vec![ + Block { + begin: 1, + len: 2, + data: vec![7], + }, + Block { + begin: 3, + len: 2, + data: vec![8], + }, + ], + out: vec![1, 7, 8], + }, + Case { + src: vec![1, 2, 3, 4, 5, 6, 7, 8], + blocks: vec![ + Block { + begin: 0, + len: 3, + data: vec![0], + }, + Block { + begin: 3, + len: 2, + data: vec![9, 9, 9, 9], + }, + Block { + begin: 5, + len: 2, + data: vec![8], + }, + Block { + begin: 7, + len: 1, + data: vec![1], + }, + ], + out: vec![0, 9, 9, 9, 9, 8, 1], + }, + ] +} #[test] fn it_change_block_buffer_size_1_byte() { @@ -1094,7 +1202,7 @@ fn it_change_block_buffer_size_1_byte() { .unwrap(); file.seek(SeekFrom::Start(0)).unwrap(); file.write_all(&case.src).unwrap(); - file.change_block(&case.block, &case.data, 1); + file.change_block(&case.blocks[0], 1); let mut buffer = vec![0; case.out.len()]; file.seek(SeekFrom::Start(0)).unwrap(); file.read(&mut buffer[..]).unwrap(); @@ -1119,7 +1227,7 @@ fn it_change_block_test_buffer_size_1_mb() { .unwrap(); file.seek(SeekFrom::Start(0)).unwrap(); file.write_all(&case.src).unwrap(); - file.change_block(&case.block, &case.data, 1024 * 1024); + file.change_block(&case.blocks[0], 1024 * 1024); let mut buffer = vec![0; case.out.len()]; file.seek(SeekFrom::Start(0)).unwrap(); file.read(&mut buffer[..]).unwrap(); @@ -1137,8 +1245,91 @@ fn it_change_block_test_out_bounds() { fs_extra::dir::create_all(&test_file.parent().unwrap(), true).unwrap(); let case = Case { src: vec![1, 2, 3, 4, 5], - block: Block { begin: 4, len: 6 }, - data: vec![7, 7, 7, 7, 7], + blocks: vec![Block { + begin: 4, + len: 6, + data: vec![7, 7, 7, 7, 7], + }], + out: vec![1, 2, 7, 7, 7, 7, 7, 5], + }; + + let mut file = OpenOptions::new() + .read(true) + .write(true) + .truncate(true) + .create(true) + .open(test_file) + .unwrap(); + file.seek(SeekFrom::Start(0)).unwrap(); + file.write_all(&case.src).unwrap(); + file.change_block(&case.blocks[0], 1024 * 1024); +} + +#[test] +fn it_change_blocks_buffer_size_1_byte() { + let cases = get_multi_cases(); + let mut test_file = std::fs::canonicalize(PathBuf::from(TEST_FOLDER)).unwrap(); + test_file.push("t_change_blocks_buffer_size_1_byte"); + test_file.push("file.db"); + fs_extra::dir::create_all(&test_file.parent().unwrap(), true).unwrap(); + for case in cases { + let mut file = OpenOptions::new() + .read(true) + .write(true) + .truncate(true) + .create(true) + .open(&test_file) + .unwrap(); + file.seek(SeekFrom::Start(0)).unwrap(); + file.write_all(&case.src).unwrap(); + file.change_blocks(case.blocks, 1); + let mut buffer = vec![0; case.out.len()]; + file.seek(SeekFrom::Start(0)).unwrap(); + file.read(&mut buffer[..]).unwrap(); + assert_eq!(case.out, buffer); + assert_eq!(file.metadata().unwrap().len(), case.out.len() as u64); + } +} +#[test] +fn it_change_blocks_test_buffer_size_1_mb() { + let cases = get_cases(); + let mut test_file = std::fs::canonicalize(PathBuf::from(TEST_FOLDER)).unwrap(); + test_file.push("it_change_blocks_test_buffer_size_1_mb"); + test_file.push("file_1mb.db"); + fs_extra::dir::create_all(&test_file.parent().unwrap(), true).unwrap(); + for case in cases { + let mut file = OpenOptions::new() + .read(true) + .write(true) + .truncate(true) + .create(true) + .open(&test_file) + .unwrap(); + file.seek(SeekFrom::Start(0)).unwrap(); + file.write_all(&case.src).unwrap(); + file.change_blocks(case.blocks, 1024 * 1024); + let mut buffer = vec![0; case.out.len()]; + file.seek(SeekFrom::Start(0)).unwrap(); + file.read(&mut buffer[..]).unwrap(); + assert_eq!(case.out, buffer); + assert_eq!(file.metadata().unwrap().len(), case.out.len() as u64); + } +} + +#[test] +#[should_panic(expected = "The selected block is out of bounds file size!")] +fn it_change_blocks_test_out_bounds() { + let mut test_file = std::fs::canonicalize(PathBuf::from(TEST_FOLDER)).unwrap(); + test_file.push("it_change_blocks_test_out_bounds"); + test_file.push("file_panic.db"); + fs_extra::dir::create_all(&test_file.parent().unwrap(), true).unwrap(); + let case = Case { + src: vec![1, 2, 3, 4, 5], + blocks: vec![Block { + begin: 4, + len: 6, + data: vec![7, 7, 7, 7, 7], + }], out: vec![1, 2, 7, 7, 7, 7, 7, 5], }; @@ -1151,5 +1342,5 @@ fn it_change_block_test_out_bounds() { .unwrap(); file.seek(SeekFrom::Start(0)).unwrap(); file.write_all(&case.src).unwrap(); - file.change_block(&case.block, &case.data, 1024 * 1024); + file.change_blocks(case.blocks, 1024 * 1024); } From 1c9ef49438f51bc78bd46d8fde28d065ede5dc6e Mon Sep 17 00:00:00 2001 From: webdesus Date: Tue, 18 Dec 2018 23:41:02 +0300 Subject: [PATCH 3/4] add aditional tests --- tests/file.rs | 75 ++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 69 insertions(+), 6 deletions(-) diff --git a/tests/file.rs b/tests/file.rs index dd9b2d9..540ad50 100644 --- a/tests/file.rs +++ b/tests/file.rs @@ -1157,7 +1157,7 @@ fn get_multi_cases() -> Vec { out: vec![1, 7, 8], }, Case { - src: vec![1, 2, 3, 4, 5, 6, 7, 8], + src: vec![1, 2, 3, 4, 5, 6, 7, 8, 9], blocks: vec![ Block { begin: 0, @@ -1165,22 +1165,85 @@ fn get_multi_cases() -> Vec { data: vec![0], }, Block { - begin: 3, + begin: 4, len: 2, data: vec![9, 9, 9, 9], }, Block { - begin: 5, + begin: 6, len: 2, data: vec![8], }, Block { - begin: 7, + begin: 8, len: 1, data: vec![1], }, ], - out: vec![0, 9, 9, 9, 9, 8, 1], + out: vec![0, 4, 9, 9, 9, 9, 8, 1], + }, + Case { + src: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 1, 1, 1, 1], + blocks: vec![ + Block { + begin: 0, + len: 2, + data: vec![0], + }, + Block { + begin: 4, + len: 2, + data: vec![9, 9, 9, 9, 9, 9], + }, + Block { + begin: 6, + len: 2, + data: vec![1], + }, + Block { + begin: 8, + len: 6, + data: vec![2], + }, + ], + out: vec![0, 3, 4, 9, 9, 9, 9, 9, 9, 1, 2], + }, + Case { + src: vec![1, 2, 3, 4, 5, 6, 7, 8, 9], + blocks: vec![ + Block { + begin: 0, + len: 2, + data: vec![0, 0, 0], + }, + Block { + begin: 4, + len: 4, + data: vec![9, 9], + }, + ], + out: vec![0, 0, 0, 3, 4, 9, 9, 9], + }, + Case { + src: vec![1, 2, 3, 4, 5, 6, 7, 8, 9], + blocks: vec![ + Block { + begin: 0, + len: 3, + data: vec![0], + }, + Block { + begin: 4, + len: 2, + data: vec![9, 9, 9, 9], + }, + Block { + begin: 6, + len: 3, + data: vec![8, 8, 8, 8], + }, + ], + out: vec![0, 4, 9, 9, 9, 9, 8, 8, 8, 8], }, ] } @@ -1287,7 +1350,7 @@ fn it_change_blocks_buffer_size_1_byte() { file.seek(SeekFrom::Start(0)).unwrap(); file.read(&mut buffer[..]).unwrap(); assert_eq!(case.out, buffer); - assert_eq!(file.metadata().unwrap().len(), case.out.len() as u64); + assert_eq!(case.out.len() as u64, file.metadata().unwrap().len()); } } #[test] From dbd2fd7d7764722e53707426b1f501e9f986b48a Mon Sep 17 00:00:00 2001 From: webdesus Date: Tue, 18 Dec 2018 23:46:13 +0300 Subject: [PATCH 4/4] change gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 378f53a..028faf3 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,5 @@ Cargo.lock /tests/temp/lib/*/* .vscode/ +.idea/