Skip to content

Instantly share code, notes, and snippets.

@almann
Last active September 1, 2020 15:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save almann/d21da1856d2a3c9f95dd04248a17d3ce to your computer and use it in GitHub Desktop.
Save almann/d21da1856d2a3c9f95dd04248a17d3ce to your computer and use it in GitHub Desktop.
Simplified Writer + Hashing Delegate
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