Last active
September 1, 2020 15:59
-
-
Save almann/d21da1856d2a3c9f95dd04248a17d3ce to your computer and use it in GitHub Desktop.
Simplified Writer + Hashing Delegate
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use byteorder::{BigEndian, ByteOrder}; | |
use sha2::{Digest, Sha256}; | |
use std::fmt::Write; | |
trait ValueWriter { | |
fn write_i64(&mut self, val: i64); | |
} | |
trait FieldWriter { | |
type MyFieldValueWriter: ValueWriter; | |
fn write_field(&mut self, field: &str, applier: impl Fn(&mut Self::MyFieldValueWriter)); | |
} | |
struct FieldContextWriter<'b, 'c, T: FieldWriter + ?Sized> { | |
field: &'c str, | |
writer: &'b mut T, | |
} | |
impl<'b, 'c, T: FieldWriter + ?Sized> FieldContextWriter<'b, 'c, T> { | |
pub fn new(writer: &'b mut T, field: &'c str) -> Self { | |
Self { field, writer } | |
} | |
} | |
impl<T: FieldWriter + ?Sized> ValueWriter for FieldContextWriter<'_, '_, T> { | |
fn write_i64(&mut self, val: i64) { | |
self.writer | |
.write_field(self.field, |v_writer| v_writer.write_i64(val)); | |
} | |
} | |
trait Writer<'a>: ValueWriter + FieldWriter { | |
fn field<'b, 'c>(&'b mut self, field: &'c str) -> FieldContextWriter<'b, 'c, Self> { | |
FieldContextWriter::new(self, field) | |
} | |
} | |
// | |
// Base Writer | |
// | |
struct MyWriter<'a, T: Write> { | |
out: &'a mut T, | |
} | |
impl<'a, T: Write> MyWriter<'a, T> { | |
pub fn new(out: &'a mut T) -> Self { | |
Self { out } | |
} | |
} | |
impl<T: Write> ValueWriter for MyWriter<'_, T> { | |
fn write_i64(&mut self, val: i64) { | |
write!(self.out, "{}, ", val).unwrap(); | |
} | |
} | |
impl<T: Write> FieldWriter for MyWriter<'_, T> { | |
type MyFieldValueWriter = Self; | |
fn write_field(&mut self, field: &str, applier: impl Fn(&mut Self::MyFieldValueWriter)) { | |
write!(self.out, "{}:", field).unwrap(); | |
applier(self); | |
} | |
} | |
impl<'a, T: Write> Writer<'a> for MyWriter<'a, T> {} | |
// | |
// Straw Man Hashing Writer | |
// | |
// NB this is modeling a reference with a raw pointer because we cannot model the | |
// generic lifetime of this reference as a Generic Associated Type, but is effectively | |
// protected by the call chain of this code. | |
struct DigestValueWriter<D: Digest, W: ValueWriter> { | |
digest: *mut D, | |
writer: *mut W, | |
} | |
impl<D: Digest, W: ValueWriter> ValueWriter for DigestValueWriter<D, W> { | |
fn write_i64(&mut self, val: i64) { | |
let mut buf = [0u8; 8]; | |
BigEndian::write_i64(&mut buf, val); | |
unsafe { | |
(*self.digest).update(&buf); | |
// delegate | |
(*self.writer).write_i64(val); | |
} | |
} | |
} | |
impl<D: Digest, W: ValueWriter> DigestValueWriter<D, W> { | |
fn new(digest: *mut D, writer: *mut W) -> Self { | |
Self { digest, writer } | |
} | |
} | |
struct DigestWriter<'a, D: Digest, W: Writer<'a>> { | |
digest: &'a mut D, | |
writer: &'a mut W, | |
} | |
impl<'a, D: Digest, W: Writer<'a>> DigestWriter<'a, D, W> { | |
fn new(digest: &'a mut D, writer: &'a mut W) -> Self { | |
Self { digest, writer } | |
} | |
} | |
impl<'a, D: Digest, W: Writer<'a>> ValueWriter for DigestWriter<'a, D, W> { | |
fn write_i64(&mut self, val: i64) { | |
DigestValueWriter::new(self.digest as *mut D, self.writer as *mut W).write_i64(val); | |
} | |
} | |
impl<'a, D: Digest, W: Writer<'a>> Writer<'a> for DigestWriter<'a, D, W> {} | |
impl<'a, D: Digest, W: Writer<'a>> FieldWriter for DigestWriter<'a, D, W> { | |
type MyFieldValueWriter = DigestValueWriter<D, W::MyFieldValueWriter>; | |
fn write_field(&mut self, field: &str, applier: impl Fn(&mut Self::MyFieldValueWriter)) { | |
self.digest.update(field.as_bytes()); | |
let digest_p = self.digest as *mut D; | |
self.writer.write_field(field, |v_writer| { | |
let mut delegate = Self::MyFieldValueWriter::new(digest_p, v_writer); | |
applier(&mut delegate); | |
}); | |
} | |
} | |
fn main() { | |
let mut sink = String::new(); | |
{ | |
let mut writer = MyWriter::new(&mut sink); | |
do_some_writing(&mut writer); | |
} | |
{ | |
let mut digest = Sha256::new(); | |
let mut writer = MyWriter::new(&mut sink); | |
let mut digest_writer = DigestWriter::new(&mut digest, &mut writer); | |
do_some_writing(&mut digest_writer); | |
const TEST_DIGEST: &[u8] = | |
b"YM\x93\xd7\xb3\x97%iW\xe8|\x9e\xcf\xe1M\x81EL\xef\x02\xdf\x1f\xbe\xc9\x153\x82\xeel\xba\x87\xf3"; | |
let hash = &digest.finalize()[..]; | |
assert_eq!(TEST_DIGEST, hash); | |
} | |
println!("WROTE: '{}'", sink); | |
} | |
fn do_some_writing<'a, W: Writer<'a>>(writer: &mut W) { | |
writer.write_i64(5); | |
writer.field("hello").write_i64(6); | |
writer.field("world").write_i64(7); | |
writer.write_i64(8); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment