text formatting

master
Avril 4 years ago
parent f6d89fdfb7
commit d2c94d0c33
Signed by: flanchan
GPG Key ID: 284488987C31F630

@ -71,6 +71,7 @@ pub struct KeyHeader
impl Header for KeyHeader
{
const CHECK: u16 = CHECK_KEY;
const NAME: &'static str = "KEY";
fn hash(&self) -> Sha256Hash
{
let mut output = Vec::new();
@ -104,13 +105,58 @@ impl KeyHeader
#[instrument(err, skip(out))]
pub async fn write_text<T: AsyncWrite+Unpin+?Sized>(&self, out: &mut T) -> Result<usize, eyre::Report>
{
todo!()
let text = serialise::into_text(self)
.wrap_err_with(|| eyre::eyre!("Failed to serialise header to text"))
.with_section(|| format!("{:?}", self).header("Header was"))?;
let mut written=0;
for bytes in text.as_bytes().chunks(16) {
out.write_all(bytes).await?;
out.write_u8(b'\n').await?;
written += bytes.len() + 1;
}
out.write_all(b"\n---").await?;
Ok(written + 4)
}
/// Read a superheader as text bytes from this stream
#[instrument(err, skip(input))]
pub async fn read_text<T: AsyncRead+Unpin+?Sized>(input: &mut T) -> Result<Self, eyre::Report>
pub async fn read_text<T: AsyncBufRead+Unpin+?Sized>(input: &mut T) -> Result<Self, eyre::Report>
{
todo!()
let (mut tx, mut rx) = mpsc::channel(1);
let line_sender = async move { //this is actually kinda overkill for this...
let mut buffer = String::new();
while input.read_line(&mut buffer).await? != 0 {
tx.send(buffer.clone());
buffer.clear();
}
Ok::<(), io::Error>(())
};
let line_reader = async {
let mut enc = String::new();
let mut had_delim =false;
while let Some(line) = rx.recv().await {
let line = line.trim();
if line == "---" {
had_delim=true;
break;
} else if line.len()>0 {
enc.push_str(line)
}
}
if !had_delim {
warn!("Buffer contained no end-of-entry delimiter");
}
//let = take_one!("Expected header line");
Ok::<Self, eyre::Report>(serialise::from_text(&enc[..])
.wrap_err_with(|| eyre::eyre!("Failed to deserialise string"))
.with_section(|| enc.header("Read string was"))?)
};
tokio::pin!(line_sender);
tokio::pin!(line_reader);
let (sres, rres) = tokio::join!(line_sender, line_reader);
sres?;
Ok(rres?)
}
/// Write this key header as bytes to this stream
#[instrument(err, skip(out))]
@ -260,13 +306,13 @@ mod tests
//color_eyre::install()?;
panic!("We're going to have to write our own text serialisation to get around the `read_whole_buffer` thingy...");
/*
/*
let header = KeyHeader::new_now(KeyHeaderKind::Aes, Default::default(), Default::default());
let mut ser = Vec::new();
let superheader = SuperHeader::<KeyHeader>::new_for(&header);
println!("Writing: {:?} + {:?}", superheader, header);
let written = superheader.write_text(&mut ser).await? +
header.write_text(&mut ser).await?;
header.write_text(&mut ser).await?;
println!("Wrote {} bytes", written);
println!("{}\n", ser.fmt_view());

@ -12,24 +12,43 @@ use tokio::{
prelude::*,
io::{
AsyncWrite,
AsyncBufRead,
AsyncRead,
},
sync::{
mpsc,
},
};
use version::Version;
/// Trait RAE headers implement
pub trait Header: fmt::Debug
{
/// Check bit for the `SuperHeader` of this header
const CHECK: u16;
/// Name for the `SuperHeader` of this header's text serialisation format
const NAME: &'static str;
/// Compute the hash of this header (used for `SuperHeader<Self>`)
fn hash(&self) -> crypto::sha256::Sha256Hash;
/// Any additional comment to be inserted into *text* output formats of `SuperHeader<Self>` only
#[inline] fn comment(&self) -> &str
{
""
}
/// Create a `SuperHeader<Self>` for this header.
#[inline(always)] fn create_super(&self) -> SuperHeader<Self>
{
SuperHeader::new_for(self)
}
}
pub const RAE_HEADER_BIT: [u8; 4] = *b"RAE0";
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
/// The header before all RAE files.
pub struct SuperHeader<H: Header>
pub struct SuperHeader<H: Header+?Sized>
{
head: [u8; 4],
vers: Version,
@ -39,7 +58,7 @@ pub struct SuperHeader<H: Header>
_header: PhantomData<H>,
}
impl<H: Header> Default for SuperHeader<H>
impl<H: Header+?Sized> Default for SuperHeader<H>
{
#[inline]
fn default() -> Self
@ -50,18 +69,71 @@ impl<H: Header> Default for SuperHeader<H>
const MAX_TEXT_SZ: Option<usize> = Some(1024 * 1024);
impl<H: Header> SuperHeader<H>
impl<H: Header +?Sized> SuperHeader<H>
{
/// Write this superheader as text bytes to this stream
#[instrument(err, skip(out))]
pub async fn write_text<T: AsyncWrite+Unpin+?Sized>(&self, out: &mut T) -> Result<usize, eyre::Report>
{
todo!()
let string = format!(r#"--- {} v{}-{:x} ({}) {} ---
"#, std::str::from_utf8(&self.head[..])
.wrap_err_with(|| eyre::eyre!("Invalid head check write"))
.with_section(|| String::from_utf8_lossy(&self.head[..]).into_owned().header("Head string was"))
.with_section(|| self.head.fmt_view().to_string().header("Head bytes were"))?,
self.vers,
self.vers.to_u32(),
self.header_hash.to_hex_string(),
H::NAME);
out.write_all(string.as_bytes()).await?;
Ok(string.len())
}
/// Read a superheader as text bytes from this stream
#[instrument(err, skip(input))]
pub async fn read_text<T: AsyncRead+Unpin+?Sized>(input: &mut T) -> Result<Self, eyre::Report>
pub async fn read_text<T: AsyncBufRead+Unpin+?Sized>(input: &mut T) -> Result<Self, eyre::Report>
{
let mut line = String::new();
input.read_line(&mut line).await?;
let line = line.trim();
trace!("Read super-header line: {:?}", line);
let mut chunks = line.split_whitespace();
let mut item = 0;
macro_rules! take_one {
() => {
if let Some(chunk) = chunks.next() {
chunk
} else {
return Err(eyre::eyre!("Not enough information encoded for this string to be valid")
.with_section(|| format!("{:#?}", chunks).header("Split section was"))
.with_note(|| "Expect at least 6 items delimited by whitespace"));
}
}
}
macro_rules! check_eq {
($val:expr) => {
{
let val = $val;
let chunk = take_one!();
if chunk != val {
return Err(eyre::eyre!("Invalid data at index {}", item)
.with_section(|| format!("{:?}", val).header("Expected"))
.with_section(|| format!("{:?}", chunk).header("Got")));
}
item +=1;
}
}
}
check_eq!("---");
check_eq!(unsafe{std::str::from_utf8_unchecked(&RAE_HEADER_BIT[..])});
//TODO: Parse text version from hex encoded integer in `2`
let version = {
let enc_str = take_one!();
// Version should be encoded like: v0.0.0*-0 (v<version string>-<version number (hex)>)
todo!()
};
//TODO: Parse hash from hex encoded string `3`
check_eq!(H::NAME);
check_eq!("---");
debug_assert_eq!(item, 6);
todo!()
}
@ -131,7 +203,7 @@ impl<H: Header> SuperHeader<H>
}
/// Verify this empty superheader
pub fn verify(&self) -> Result<(), VerificationError>
pub fn verify(&self) -> Result<(), VerificationError<H>>
{
macro_rules! check {
($field:expr, $err:expr) => {
@ -142,7 +214,7 @@ impl<H: Header> SuperHeader<H>
}
check!(self.head == RAE_HEADER_BIT, VerificationError::BadHeaderBit);
check!(self.vers.is_compat(&CURRENT_VERSION), VerificationError::IncompatableVersion(self.vers));
check!(self.chk == H::CHECK, VerificationError::BadCheckBit);
check!(self.chk == H::CHECK, VerificationError::BadCheckBit(PhantomData));
Ok(())
}
@ -151,7 +223,7 @@ impl<H: Header> SuperHeader<H>
///
/// # Notes
/// This also calls `self.verify()`.
pub fn verify_for(&self, header: &H) -> Result<(), VerificationError>
pub fn verify_for(&self, header: &H) -> Result<(), VerificationError<H>>
{
self.verify()?;
if self.header_hash == header.hash() {
@ -162,25 +234,29 @@ impl<H: Header> SuperHeader<H>
}
}
/// Error returned when `SuperHeader` fails to verify.
///
/// `H` is the header type that we were attempting to verify for
#[derive(Debug)]
#[non_exhaustive]
pub enum VerificationError
pub enum VerificationError<H: Header+?Sized>
{
BadHeaderBit,
IncompatableVersion(Version),
BadCheckBit,
BadCheckBit(PhantomData<H>),
BadHash,
}
impl error::Error for VerificationError{}
impl fmt::Display for VerificationError
impl<H: Header+?Sized> error::Error for VerificationError<H>{}
impl<H: Header+?Sized> fmt::Display for VerificationError<H>
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "failed to verify header for {}: ", std::any::type_name::<H>())?;
match self {
Self::BadHeaderBit => write!(f, "bad header bit"),
Self::IncompatableVersion(vers) => write!(f, "contained incompatable version {}, we are on version {}", vers, CURRENT_VERSION),
Self::BadCheckBit => write!(f, "contained invalid check bit"),
Self::BadCheckBit(_) => write!(f, "contained invalid check bit"),
Self::BadHash => write!(f, "contained invalid header hash"),
}
}

@ -1,6 +1,10 @@
#![cfg_attr(nightly, feature(label_break_value))]
#![cfg_attr(nightly, feature(label_break_value))]
#![cfg_attr(nightly, feature(const_fn))]
#![cfg_attr(nightly, feature(never_type))]
#![allow(dead_code)]
#![allow(unused_macros)]
#[macro_use] extern crate tracing;
#[macro_use] extern crate pin_project;
@ -32,6 +36,32 @@ macro_rules! cfg_debug {
}
};
}
macro_rules! nightly {
(else $($else:tt)*) => {
cfg_if::cfg_if! {
if #[cfg(not(nightly))] {
$($else)*
}
}
};
(if {$($if:tt)*} else {$($else:tt)*}) => {
cfg_if::cfg_if!{
if #[cfg(nightly)] {
$($if)*
} else {
$($else)*
}
}
};
($($tt:tt)*) => {
cfg_if::cfg_if! {
if #[cfg(nightly)] {
$($tt)*
}
}
}
}
macro_rules! static_assert {
($expr:expr) => {

@ -86,7 +86,8 @@ impl Default for Tag
#[repr(align(4))]
pub struct Version(u8,u8,u8,Tag);
static_assert!(std::mem::size_of::<Version>() == std::mem::size_of::<[u8; 4]>(),
"Size of version != size of [u8; 4]");
static_assert!(std::mem::size_of::<Version>() == std::mem::size_of::<u32>(),
"Size of version != size of u32");
static_assert!(std::mem::size_of::<Version>() == 4,
@ -94,6 +95,7 @@ static_assert!(std::mem::size_of::<Version>() == 4,
static_assert!(std::mem::align_of::<Version>() == std::mem::align_of::<u32>(),
"Align of version != align of u32 with `repr(align(4))`");
impl fmt::Display for Version
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
@ -140,17 +142,32 @@ impl Version
}
/// Uncheckedly create a version from bytes
pub unsafe fn from_bytes_unchecked(bytes: [u8; 4]) -> Self
#[cfg(nightly)]
#[inline(always)] pub const unsafe fn from_bytes_unchecked(bytes: [u8; 4]) -> Self
{
std::mem::transmute(bytes)
}
/// Uncheckedly create a version from bytes
#[cfg(not(nightly))]
#[inline(always)] pub unsafe fn from_bytes_unchecked(bytes: [u8; 4]) -> Self
{
let mut s = Self::default();
assert_eq!(bytes::copy_slice(s.as_bytes_mut(), &bytes[..]), std::mem::size_of::<Self>());
debug_assert_eq!(bytes::copy_slice(s.as_bytes_mut(), &bytes[..]), std::mem::size_of::<Self>());
s
}
/// Convert into 4 byte array
#[inline] pub fn into_bytes(self) -> [u8; 4]
#[inline(always)] pub const fn to_bytes(self) -> [u8; 4]
{
[self.0, self.1, self.2, self.3 as u8]
nightly! {
if {
unsafe {std::mem::transmute(self)} // we already statically asserted this to be okay
}
else {
[self.0, self.1, self.2, self.3 as u8]
}
}
}
@ -161,19 +178,41 @@ impl Version
}
/// Encode as u32
pub fn as_u32(&self) -> u32
#[inline] pub fn as_u32(&self) -> u32
{
debug_assert_eq!(size_of::<Self>(), size_of::<u32>());
unsafe{*(self as *const Self as *const u32)}
nightly! {
if {
self.to_u32()
} else {
debug_assert_eq!(size_of::<Self>(), size_of::<u32>());
unsafe{*(self as *const Self as *const u32)}
}
}
}
/// Convert this `Version` directly to `u32`.
#[inline(always)] pub const fn to_u32(self) -> u32
{
unsafe {
std::mem::transmute(self) //we have already statically asserted that this is safe
}
}
/// Decode u32 as self, assuming `Tag` is valid.
pub unsafe fn from_u32_unchecked(from: u32) -> Self
#[cfg(nightly)]
#[inline(always)] pub const unsafe fn from_u32_unchecked(from: u32) -> Self
{
std::mem::transmute(from)
}
#[cfg(not(nightly))]
#[inline(always)] pub unsafe fn from_u32_unchecked(from:u32) -> Self
{
debug_assert_eq!(size_of::<Self>(), size_of::<u32>());
*(&from as *const u32 as *const Self)
}
/// Try to parse a `u32` encoded `Version`.
pub fn try_from_u32(from: u32) -> Result<Self, ParsingError>
{

Loading…
Cancel
Save