flipfile/
lib.rs

1//! Flip the bytes in multiple files.
2//!
3//! This crate ships with a default binary `flipfile` that lets you flip multiple files.
4
5#![cfg_attr(doc_cfg, feature(doc_cfg))]
6
7use std::fs::File;
8use std::io::{Read, Seek, SeekFrom, Write};
9
10/// Operations to apply to every byte.
11#[derive(Debug, Default)]
12pub struct Operations {
13    /// Flip the bytes, i.e. negate each bit.
14    pub flip: bool,
15    /// Reverse the bytes.
16    pub reverse: bool,
17    /// Swab the bytes, i.e. swap the first 4 and the last 4 bits.
18    pub swab: bool,
19}
20
21/// Transform each byte in `buffer` according to the operations specified in `ops`.
22pub fn process_buffer(buffer: &mut [u8], ops: &Operations) {
23    for b in buffer.iter_mut() {
24        if ops.flip {
25            *b = !*b;
26        }
27
28        if ops.reverse & ops.swab {
29            let mut t = *b;
30            t = (t & 0xCC) >> 2 | (t & 0x33) << 2;
31            t = (t & 0xAA) >> 1 | (t & 0x55) << 1;
32            *b = t;
33        } else if ops.reverse {
34            let mut t = *b;
35            t = (t & 0xF0) >> 4 | (t & 0x0F) << 4;
36            t = (t & 0xCC) >> 2 | (t & 0x33) << 2;
37            t = (t & 0xAA) >> 1 | (t & 0x55) << 1;
38            *b = t;
39        } else if ops.swab {
40            let mut t = *b;
41            t = (t & 0xF0) >> 4 | (t & 0x0F) << 4;
42            *b = t;
43        }
44    }
45}
46
47/// Transform each byte in `file` according to the operations specified in `ops`.
48///
49/// The file is read through a buffer, which is then transformed via
50/// [`process_buffer`](process_buffer) and written back to the file.
51pub fn process_file(file: &mut File, ops: &Operations) -> std::io::Result<u64> {
52    log::debug!("ops = {:?}", ops);
53
54    let mut nflipped = 0;
55    let mut buffer = [0; 1024 * 256];
56    loop {
57        let pos = file.stream_position()?;
58        let nread = file.read(&mut buffer)?;
59
60        log::debug!("pos = {}, nread = {}", pos, nread);
61        debug_assert_eq!(pos + nread as u64, file.stream_position()?);
62
63        if nread == 0 {
64            break;
65        }
66
67        process_buffer(&mut buffer[0..nread], ops);
68
69        file.seek(SeekFrom::Start(pos))?;
70
71        let nwritten = file.write(&buffer[0..nread])?;
72
73        log::trace!("nwritten = {}", nwritten);
74        debug_assert_eq!(nread, nwritten);
75
76        nflipped += nread as u64;
77    }
78    Ok(nflipped)
79}
80
81/// Transform each byte in `file` according to the operations specified in `ops`.
82///
83/// The file is memory mapped and transformed via [`process_buffer`](process_buffer).
84#[cfg(feature = "memmap")]
85#[cfg_attr(doc_cfg, doc(cfg(feature = "memmap")))]
86pub fn process_file_mmap(file: &mut File, ops: &Operations) -> std::io::Result<u64> {
87    let mut mmap = unsafe { memmap::MmapMut::map_mut(&file)? };
88
89    process_buffer(&mut mmap, ops);
90
91    Ok(mmap.len() as u64)
92}
93
94#[deprecated = "use [`process_file`](process_file) instead"]
95pub fn flip_file(file: &mut File) -> std::io::Result<u64> {
96    let mut nflipped = 0;
97    let mut buffer = [0; 1024 * 256];
98    loop {
99        let pos = file.stream_position()?;
100        let nread = file.read(&mut buffer)?;
101
102        log::debug!("pos = {}, nread = {}", pos, nread);
103        debug_assert_eq!(pos + nread as u64, file.stream_position()?);
104
105        if nread == 0 {
106            break;
107        }
108
109        for i in 0..nread {
110            buffer[i] = !buffer[i];
111        }
112
113        file.seek(SeekFrom::Start(pos))?;
114
115        let nwritten = file.write(&buffer[0..nread])?;
116
117        log::trace!("nwritten = {}", nwritten);
118        debug_assert_eq!(nread, nwritten);
119
120        nflipped += nread as u64;
121    }
122    Ok(nflipped)
123}
124
125#[deprecated = "use [`process_file_mmap`](process_file_mmap) instead"]
126#[cfg(feature = "memmap")]
127#[cfg_attr(doc_cfg, doc(cfg(feature = "memmap")))]
128pub fn flip_file_mmap(file: &mut File) -> std::io::Result<u64> {
129    let mut mmap = unsafe { memmap::MmapMut::map_mut(&file)? };
130
131    for b in mmap.iter_mut() {
132        *b = !*b;
133    }
134
135    Ok(mmap.len() as u64)
136}
137
138#[cfg(test)]
139mod tests {
140    use super::*;
141
142    #[test]
143    #[allow(deprecated)]
144    fn test_flip_file() -> std::io::Result<()> {
145        let mut file = tempfile::tempfile()?;
146        for i in 0..255 {
147            file.write(&[i])?;
148        }
149        file.seek(SeekFrom::Start(0))?;
150        flip_file(&mut file)?;
151        file.seek(SeekFrom::Start(0))?;
152        for i in 0..255 {
153            let buf = &mut [0];
154            file.read(buf)?;
155            assert_eq!(buf[0], !i);
156        }
157        Ok(())
158    }
159
160    #[test]
161    #[cfg(feature = "memmap")]
162    #[allow(deprecated)]
163    fn test_flip_file_mmap() -> std::io::Result<()> {
164        let mut file = tempfile::tempfile()?;
165        for i in 0..255 {
166            file.write(&[i])?;
167        }
168        file.seek(SeekFrom::Start(0))?;
169        flip_file_mmap(&mut file)?;
170        file.seek(SeekFrom::Start(0))?;
171        for i in 0..255 {
172            let buf = &mut [0];
173            file.read(buf)?;
174            assert_eq!(buf[0], !i);
175        }
176        Ok(())
177    }
178}