#[cfg(feature = "alloc")]
use alloc::{string::String, vec::Vec};
#[cfg(feature = "std")]
use std::{
fs::File,
io::{Read, Write},
path::Path,
};
#[cfg(all(unix, feature = "std"))]
use std::{fs::OpenOptions, os::unix::fs::OpenOptionsExt};
#[cfg(feature = "std")]
use zeroize::Zeroize;
use super::Error;
#[cfg(unix)]
pub const FILE_MODE: u32 = 0o600;
pub trait Encoding: Send + Sync {
fn encode_to_slice(&self, src: &[u8], dst: &mut [u8]) -> Result<usize, Error>;
fn encoded_len(&self, bytes: &[u8]) -> usize;
#[cfg(feature = "alloc")]
fn encode<B: AsRef<[u8]>>(&self, bytes: B) -> Vec<u8> {
let expected_length = self.encoded_len(bytes.as_ref());
let mut encoded = vec![0u8; expected_length];
let actual_length = self.encode_to_slice(bytes.as_ref(), &mut encoded).unwrap();
debug_assert_eq!(expected_length, actual_length);
encoded
}
#[cfg(feature = "alloc")]
fn encode_to_string<B: AsRef<[u8]>>(&self, bytes: B) -> Result<String, Error> {
Ok(String::from_utf8(self.encode(bytes))?)
}
#[cfg(feature = "std")]
fn encode_to_writer<B, W>(&self, bytes: B, writer: &mut W) -> Result<usize, Error>
where
B: AsRef<[u8]>,
W: Write,
{
let mut encoded_bytes = self.encode(bytes);
writer.write_all(encoded_bytes.as_ref())?;
encoded_bytes.zeroize();
Ok(encoded_bytes.len())
}
#[cfg(all(unix, feature = "std"))]
fn encode_to_file<B, P>(&self, bytes: B, path: P) -> Result<File, Error>
where
B: AsRef<[u8]>,
P: AsRef<Path>,
{
let mut file = OpenOptions::new()
.create(true)
.write(true)
.truncate(true)
.mode(FILE_MODE)
.open(path)?;
self.encode_to_writer(bytes, &mut file)?;
Ok(file)
}
#[cfg(all(not(unix), feature = "std"))]
fn encode_to_file<B, P>(&self, bytes: B, path: P) -> Result<File, Error>
where
B: AsRef<[u8]>,
P: AsRef<Path>,
{
let mut file = File::create(path.as_ref())?;
self.encode_to_writer(bytes, &mut file)?;
Ok(file)
}
fn decode_to_slice(&self, src: &[u8], dst: &mut [u8]) -> Result<usize, Error>;
fn decoded_len(&self, encoded_bytes: &[u8]) -> Result<usize, Error>;
#[cfg(feature = "alloc")]
fn decode<B: AsRef<[u8]>>(&self, encoded_bytes: B) -> Result<Vec<u8>, Error> {
let expected_length = self.decoded_len(encoded_bytes.as_ref())?;
let mut decoded = vec![0u8; expected_length];
let actual_length = self.decode_to_slice(encoded_bytes.as_ref(), &mut decoded)?;
debug_assert_eq!(expected_length, actual_length);
Ok(decoded)
}
#[cfg(feature = "std")]
fn decode_from_str<S: AsRef<str>>(&self, encoded: S) -> Result<Vec<u8>, Error> {
self.decode(encoded.as_ref().as_bytes())
}
#[cfg(feature = "std")]
fn decode_from_reader<R: Read>(&self, reader: &mut R) -> Result<Vec<u8>, Error> {
let mut bytes = vec![];
reader.read_to_end(bytes.as_mut())?;
let result = self.decode(&bytes);
bytes.zeroize();
result
}
#[cfg(feature = "std")]
fn decode_from_file<P: AsRef<Path>>(&self, path: P) -> Result<Vec<u8>, Error> {
self.decode_from_reader(&mut File::open(path.as_ref())?)
}
}
#[cfg(feature = "alloc")]
#[cfg(test)]
mod tests {
use super::*;
const TEST_DATA: &[u8] = b"Testing 1, 2, 3...";
struct TestEncoding {}
impl Default for TestEncoding {
fn default() -> Self {
Self {}
}
}
impl Encoding for TestEncoding {
fn encode_to_slice(&self, src: &[u8], dst: &mut [u8]) -> Result<usize, Error> {
let length = self.encoded_len(src);
assert_eq!(dst.len(), length);
Ok(length)
}
fn encoded_len(&self, bytes: &[u8]) -> usize {
bytes.len() * 4 / 3
}
fn decode_to_slice(&self, src: &[u8], dst: &mut [u8]) -> Result<usize, Error> {
let length = self.decoded_len(src)?;
assert_eq!(dst.len(), length);
Ok(length)
}
fn decoded_len(&self, bytes: &[u8]) -> Result<usize, Error> {
Ok(bytes.len() * 3 / 4)
}
}
#[test]
fn test_encode() {
TestEncoding::default().encode(TEST_DATA);
}
#[test]
fn test_decode() {
let encoding = TestEncoding::default();
let encoded = encoding.encode(TEST_DATA);
encoding.decode(&encoded).unwrap();
}
}