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 impl Header for KeyHeader
{ {
const CHECK: u16 = CHECK_KEY; const CHECK: u16 = CHECK_KEY;
const NAME: &'static str = "KEY";
fn hash(&self) -> Sha256Hash fn hash(&self) -> Sha256Hash
{ {
let mut output = Vec::new(); let mut output = Vec::new();
@ -104,13 +105,58 @@ impl KeyHeader
#[instrument(err, skip(out))] #[instrument(err, skip(out))]
pub async fn write_text<T: AsyncWrite+Unpin+?Sized>(&self, out: &mut T) -> Result<usize, eyre::Report> 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 /// Read a superheader as text bytes from this stream
#[instrument(err, skip(input))] #[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 /// Write this key header as bytes to this stream
#[instrument(err, skip(out))] #[instrument(err, skip(out))]

@ -12,24 +12,43 @@ use tokio::{
prelude::*, prelude::*,
io::{ io::{
AsyncWrite, AsyncWrite,
AsyncBufRead,
AsyncRead, AsyncRead,
}, },
sync::{
mpsc,
},
}; };
use version::Version; use version::Version;
/// Trait RAE headers implement
pub trait Header: fmt::Debug pub trait Header: fmt::Debug
{ {
/// Check bit for the `SuperHeader` of this header
const CHECK: u16; 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; 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"; pub const RAE_HEADER_BIT: [u8; 4] = *b"RAE0";
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
/// The header before all RAE files. /// The header before all RAE files.
pub struct SuperHeader<H: Header> pub struct SuperHeader<H: Header+?Sized>
{ {
head: [u8; 4], head: [u8; 4],
vers: Version, vers: Version,
@ -39,7 +58,7 @@ pub struct SuperHeader<H: Header>
_header: PhantomData<H>, _header: PhantomData<H>,
} }
impl<H: Header> Default for SuperHeader<H> impl<H: Header+?Sized> Default for SuperHeader<H>
{ {
#[inline] #[inline]
fn default() -> Self fn default() -> Self
@ -50,18 +69,71 @@ impl<H: Header> Default for SuperHeader<H>
const MAX_TEXT_SZ: Option<usize> = Some(1024 * 1024); 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 /// Write this superheader as text bytes to this stream
#[instrument(err, skip(out))] #[instrument(err, skip(out))]
pub async fn write_text<T: AsyncWrite+Unpin+?Sized>(&self, out: &mut T) -> Result<usize, eyre::Report> 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 /// Read a superheader as text bytes from this stream
#[instrument(err, skip(input))] #[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!() todo!()
} }
@ -131,7 +203,7 @@ impl<H: Header> SuperHeader<H>
} }
/// Verify this empty superheader /// Verify this empty superheader
pub fn verify(&self) -> Result<(), VerificationError> pub fn verify(&self) -> Result<(), VerificationError<H>>
{ {
macro_rules! check { macro_rules! check {
($field:expr, $err:expr) => { ($field:expr, $err:expr) => {
@ -142,7 +214,7 @@ impl<H: Header> SuperHeader<H>
} }
check!(self.head == RAE_HEADER_BIT, VerificationError::BadHeaderBit); check!(self.head == RAE_HEADER_BIT, VerificationError::BadHeaderBit);
check!(self.vers.is_compat(&CURRENT_VERSION), VerificationError::IncompatableVersion(self.vers)); 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(()) Ok(())
} }
@ -151,7 +223,7 @@ impl<H: Header> SuperHeader<H>
/// ///
/// # Notes /// # Notes
/// This also calls `self.verify()`. /// 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()?; self.verify()?;
if self.header_hash == header.hash() { 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)] #[derive(Debug)]
#[non_exhaustive] #[non_exhaustive]
pub enum VerificationError pub enum VerificationError<H: Header+?Sized>
{ {
BadHeaderBit, BadHeaderBit,
IncompatableVersion(Version), IncompatableVersion(Version),
BadCheckBit, BadCheckBit(PhantomData<H>),
BadHash, BadHash,
} }
impl error::Error for VerificationError{} impl<H: Header+?Sized> error::Error for VerificationError<H>{}
impl fmt::Display for VerificationError impl<H: Header+?Sized> fmt::Display for VerificationError<H>
{ {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{ {
write!(f, "failed to verify header for {}: ", std::any::type_name::<H>())?;
match self { match self {
Self::BadHeaderBit => write!(f, "bad header bit"), Self::BadHeaderBit => write!(f, "bad header bit"),
Self::IncompatableVersion(vers) => write!(f, "contained incompatable version {}, we are on version {}", vers, CURRENT_VERSION), 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"), 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(dead_code)]
#![allow(unused_macros)]
#[macro_use] extern crate tracing; #[macro_use] extern crate tracing;
#[macro_use] extern crate pin_project; #[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 { macro_rules! static_assert {
($expr:expr) => { ($expr:expr) => {

@ -86,7 +86,8 @@ impl Default for Tag
#[repr(align(4))] #[repr(align(4))]
pub struct Version(u8,u8,u8,Tag); 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>(), static_assert!(std::mem::size_of::<Version>() == std::mem::size_of::<u32>(),
"Size of version != size of u32"); "Size of version != size of u32");
static_assert!(std::mem::size_of::<Version>() == 4, 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>(), static_assert!(std::mem::align_of::<Version>() == std::mem::align_of::<u32>(),
"Align of version != align of u32 with `repr(align(4))`"); "Align of version != align of u32 with `repr(align(4))`");
impl fmt::Display for Version impl fmt::Display for Version
{ {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
@ -140,18 +142,33 @@ impl Version
} }
/// Uncheckedly create a version from bytes /// 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(); 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 s
} }
/// Convert into 4 byte array /// Convert into 4 byte array
#[inline] pub fn into_bytes(self) -> [u8; 4] #[inline(always)] pub const fn to_bytes(self) -> [u8; 4]
{ {
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] [self.0, self.1, self.2, self.3 as u8]
} }
}
}
/// Get mutable binary representation (4 bytes) /// Get mutable binary representation (4 bytes)
@ -161,19 +178,41 @@ impl Version
} }
/// Encode as u32 /// Encode as u32
pub fn as_u32(&self) -> u32 #[inline] pub fn as_u32(&self) -> u32
{ {
nightly! {
if {
self.to_u32()
} else {
debug_assert_eq!(size_of::<Self>(), size_of::<u32>()); debug_assert_eq!(size_of::<Self>(), size_of::<u32>());
unsafe{*(self as *const Self as *const 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. /// 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) *(&from as *const u32 as *const Self)
} }
/// Try to parse a `u32` encoded `Version`. /// Try to parse a `u32` encoded `Version`.
pub fn try_from_u32(from: u32) -> Result<Self, ParsingError> pub fn try_from_u32(from: u32) -> Result<Self, ParsingError>
{ {

Loading…
Cancel
Save