xorio/lib.rs
1//! [`Read`]/[`Write`] implementation that Xor's the bytes that come through it and wraps around
2//! another [`Read`] or [`Write`].
3//!
4//! # Examples
5//!
6//! ## Writing
7//!
8//! ```no_run
9//! # use xorio::Xor;
10//! # use std::fs::File;
11//! # use std::io::Write;
12//! let mut file = File::create("my_xored_file.bin").unwrap();
13//! let mut writer = Xor::new(file);
14//! writer.write_all("Hello World".as_bytes());
15//! ```
16//!
17//! ## Reading
18//!
19//! ```no_run
20//! # use xorio::Xor;
21//! # use std::fs::File;
22//! # use std::io::Read;
23//! let mut file = File::open("my_xored_file.bin").unwrap();
24//! let mut reader = Xor::new(file);
25//! let mut content = String::new();
26//! reader.read_to_string(&mut content);
27//! ```
28//!
29//! ## Custom Xor Bytes
30//!
31//! You can also customize the bytes that it will XOR the stream with. By default it uses a single
32//! byte `0b01010101` to calculate the XOR.
33//!
34//! ```no_run
35//! # use xorio::Xor;
36//! # use std::fs::File;
37//! # use std::io::Write;
38//! let mut file = File::create("my_xored_file.bin").unwrap();
39//! let mut writer = Xor::new_with_xor_bytes(file, vec![1, 2, 3]);
40//! writer.write_all("Hello World".as_bytes());
41//! ```
42//!
43//! # License
44//!
45//! This crate is licensed under the [Katharos License][k_license] which places certain
46//! restrictions on what you are allowed to use it for. Please read and understand the terms before
47//! using this crate for your project.
48//!
49//! [k_license]: https://github.com/katharostech/katharos-license
50
51use std::io::{Read, Seek, Write};
52
53/// [`Read`]/[`Write`] implementation that Xor's the bytes that come through it and wraps around
54/// another [`Read`] or [`Write`].
55pub struct Xor<T> {
56 inner: T,
57 xor_bytes: Vec<u8>,
58}
59
60impl<T> Xor<T> {
61 /// Create a new [`Xor`] wrapped around the given IO type.
62 pub fn new(inner: T) -> Self {
63 Xor {
64 inner,
65 xor_bytes: vec![0b01010101],
66 }
67 }
68
69 /// Create a new [`Xor`] wrapped around the given IO type, with custom `xor_bytes` that will be
70 /// used when XOR-ing the stream. The default `xor_bytes` is one `0b01010101` byte.
71 pub fn new_with_xor_bytes(inner: T, xor_bytes: Vec<u8>) -> Self {
72 Xor { inner, xor_bytes }
73 }
74}
75
76impl<R: Read> Read for Xor<R> {
77 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
78 // Read bytes into buf
79 let count = self.inner.read(buf)?;
80
81 // Xor the bytes in the buffer
82 for byte in buf {
83 for xor_byte in &self.xor_bytes {
84 *byte = *byte ^ xor_byte;
85 }
86 }
87
88 Ok(count)
89 }
90}
91
92impl<W: Write> Write for Xor<W> {
93 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
94 self.inner.write(
95 // Write the buffer with each byte XOR-ed
96 buf.iter()
97 .map(|x| {
98 let mut byte = *x;
99 for xor_byte in &self.xor_bytes {
100 byte = byte ^ xor_byte
101 }
102 byte
103 })
104 .collect::<Vec<_>>()
105 .as_slice(),
106 )
107 }
108
109 fn flush(&mut self) -> std::io::Result<()> {
110 self.inner.flush()
111 }
112}
113
114impl<S: Seek> Seek for Xor<S> {
115 fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> {
116 self.inner.seek(pos)
117 }
118}