From 9f26e0b972d6a59c6927e620369875deb779e536 Mon Sep 17 00:00:00 2001 From: Christian Lewe Date: Fri, 17 Nov 2023 11:40:07 +0100 Subject: [PATCH 1/4] Encode: Return number of written bits Every public method should return the number of written bits. --- src/bit_encoding/encode.rs | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/bit_encoding/encode.rs b/src/bit_encoding/encode.rs index af532f0b..8575ccb6 100644 --- a/src/bit_encoding/encode.rs +++ b/src/bit_encoding/encode.rs @@ -173,14 +173,14 @@ pub fn encode_program( let iter = EncodeNode::Node(program).post_order_iter::>(); let len = iter.clone().count(); - let start_n = w.n_total_written(); + let n_start = w.n_total_written(); encode_natural(len, w)?; for node in iter { encode_node(node, w)?; } - Ok(w.n_total_written() - start_n) + Ok(w.n_total_written() - n_start) } /// Encode a node to bits. @@ -291,7 +291,7 @@ where I: Iterator + Clone, { let mut bit_len = 0; - let start_n = w.n_total_written(); + let n_start = w.n_total_written(); for value in witness.clone() { bit_len += value.len(); @@ -308,11 +308,13 @@ where } } - Ok(w.n_total_written() - start_n) + Ok(w.n_total_written() - n_start) } /// Encode a value to bits. -pub fn encode_value(value: &Value, w: &mut BitWriter) -> io::Result<()> { +pub fn encode_value(value: &Value, w: &mut BitWriter) -> io::Result { + let n_start = w.n_total_written(); + match value { Value::Unit => {} Value::SumL(left) => { @@ -329,21 +331,22 @@ pub fn encode_value(value: &Value, w: &mut BitWriter) -> io::Re } } - Ok(()) + Ok(w.n_total_written() - n_start) } /// Encode a hash to bits. -pub fn encode_hash(h: &[u8], w: &mut BitWriter) -> io::Result<()> { +pub fn encode_hash(h: &[u8], w: &mut BitWriter) -> io::Result { for byte in h { w.write_bits_be(u64::from(*byte), 8)?; } - Ok(()) + Ok(h.len() * 8) } /// Encode a natural number to bits. -pub fn encode_natural(n: usize, w: &mut BitWriter) -> io::Result<()> { +pub fn encode_natural(n: usize, w: &mut BitWriter) -> io::Result { assert_ne!(n, 0); // Cannot encode zero + let n_start = w.n_total_written(); let len = 8 * mem::size_of::() - n.leading_zeros() as usize - 1; if len == 0 { @@ -354,7 +357,7 @@ pub fn encode_natural(n: usize, w: &mut BitWriter) -> io::Resul w.write_bits_be(n as u64, len)?; } - Ok(()) + Ok(w.n_total_written() - n_start) } #[cfg(test)] From 3b7f240910f54a74756cd00c12931b6df3ac85eb Mon Sep 17 00:00:00 2001 From: Christian Lewe Date: Fri, 17 Nov 2023 13:10:10 +0100 Subject: [PATCH 2/4] Encode: Rewrite encode_natural The new algorithm is nonrecursive. --- src/bit_encoding/encode.rs | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/src/bit_encoding/encode.rs b/src/bit_encoding/encode.rs index 8575ccb6..5e8ddf4d 100644 --- a/src/bit_encoding/encode.rs +++ b/src/bit_encoding/encode.rs @@ -343,18 +343,34 @@ pub fn encode_hash(h: &[u8], w: &mut BitWriter) -> io::Result(n: usize, w: &mut BitWriter) -> io::Result { - assert_ne!(n, 0); // Cannot encode zero +/// Encode a positive integer to bits. +pub fn encode_natural(mut n: usize, w: &mut BitWriter) -> io::Result { + assert!(n > 0, "Zero cannot be encoded"); let n_start = w.n_total_written(); - let len = 8 * mem::size_of::() - n.leading_zeros() as usize - 1; - if len == 0 { - w.write_bit(false)?; - } else { - w.write_bit(true)?; - encode_natural(len, w)?; - w.write_bits_be(n as u64, len)?; + /// Minimum number of bits to represent `n` minus the most-significant bit + fn truncated_bit_len(n: usize) -> usize { + 8 * mem::size_of::() - n.leading_zeros() as usize - 1 + } + + let mut suffix = Vec::new(); + + loop { + debug_assert!(n > 0); + let len = truncated_bit_len(n); + if len == 0 { + w.write_bit(false)?; + break; + } else { + w.write_bit(true)?; + suffix.push((n, len)); + n = len; + } + } + + while let Some((bits, len)) = suffix.pop() { + let bits = bits as u64; // Case safety: assuming 64-bit machine or lower + w.write_bits_be(bits, len)?; } Ok(w.n_total_written() - n_start) From cfaf79408b1235554027483f57900a6d9919ef38 Mon Sep 17 00:00:00 2001 From: Christian Lewe Date: Tue, 21 Nov 2023 22:05:15 +0100 Subject: [PATCH 3/4] Encode: Add write_to_vec We often write bits to a byte vector. write_to_vec generalizes this workflow by writing the result of any bit operation into a byte vector, which it returns. The caller doesn't have to worry about I/O errors that can never occur. --- src/bit_encoding/bitwriter.rs | 35 +++++++++++++++++++++++++++++++++++ src/bit_encoding/mod.rs | 2 +- src/lib.rs | 2 +- 3 files changed, 37 insertions(+), 2 deletions(-) diff --git a/src/bit_encoding/bitwriter.rs b/src/bit_encoding/bitwriter.rs index a6a38fc0..eb3fcba1 100644 --- a/src/bit_encoding/bitwriter.rs +++ b/src/bit_encoding/bitwriter.rs @@ -109,3 +109,38 @@ impl BitWriter { Ok(len) } } + +/// Write the result of a bit operation into a byte vector and return the vector. +/// +/// I/O to a vector never fails. +pub fn write_to_vec(f: F) -> Vec +where + F: FnOnce(&mut BitWriter<&mut Vec>) -> io::Result, +{ + let mut bytes = Vec::new(); + let mut bits = BitWriter::new(&mut bytes); + f(&mut bits).expect("I/O to vector never fails"); + bits.flush_all().expect("I/O to vector never fails"); + bytes +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::jet::Core; + use crate::node::CoreConstructible; + use crate::ConstructNode; + use std::sync::Arc; + + #[test] + fn vec() { + let program = Arc::>::unit(); + let _ = write_to_vec(|w| program.encode(w)); + } + + #[test] + fn empty_vec() { + let vec = write_to_vec(|_| Ok(0)); + assert!(vec.is_empty()); + } +} diff --git a/src/bit_encoding/mod.rs b/src/bit_encoding/mod.rs index 28961559..468617d0 100644 --- a/src/bit_encoding/mod.rs +++ b/src/bit_encoding/mod.rs @@ -28,4 +28,4 @@ pub mod decode; pub mod encode; pub use bititer::{BitIter, EarlyEndOfStreamError}; -pub use bitwriter::BitWriter; +pub use bitwriter::{write_to_vec, BitWriter}; diff --git a/src/lib.rs b/src/lib.rs index 950aea38..52e91756 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -51,7 +51,7 @@ mod value; pub use bit_encoding::decode; pub use bit_encoding::encode; -pub use bit_encoding::BitWriter; +pub use bit_encoding::{write_to_vec, BitWriter}; pub use bit_encoding::{BitIter, EarlyEndOfStreamError}; #[cfg(feature = "elements")] From 5a75b547740f228daa58ae7050fc32302067d68d Mon Sep 17 00:00:00 2001 From: Christian Lewe Date: Tue, 5 Dec 2023 13:51:01 +0100 Subject: [PATCH 4/4] Node: Update RedeemNode::encode_to_vec --- src/node/redeem.rs | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/node/redeem.rs b/src/node/redeem.rs index f97085c6..b867c1a3 100644 --- a/src/node/redeem.rs +++ b/src/node/redeem.rs @@ -16,7 +16,7 @@ use crate::analysis::NodeBounds; use crate::dag::{DagLike, InternalSharing, MaxSharing, PostOrderIterItem}; use crate::jet::Jet; use crate::types::{self, arrow::FinalArrow}; -use crate::{encode, WitnessNode}; +use crate::{encode, write_to_vec, WitnessNode}; use crate::{Amr, BitIter, BitWriter, Cmr, Error, FirstPassImr, Imr, Value}; use super::{ @@ -364,7 +364,9 @@ impl RedeemNode { Ok(program) } - /// Encode a Simplicity program to bits, including the witness data. + /// Encode the program to bits. + /// + /// Includes witness data. Returns the number of written bits. pub fn encode(&self, w: &mut BitWriter) -> io::Result { let sharing_iter = self.post_order_iter::>>(); let program_bits = encode::encode_program(self, w)?; @@ -374,15 +376,11 @@ impl RedeemNode { Ok(program_bits + witness_bits) } - /// Encode a Simplicity program to a vector of bytes, including the witness data. + /// Encode the program to a byte vector. + /// + /// Includes witness data. pub fn encode_to_vec(&self) -> Vec { - let mut program_and_witness_bytes = Vec::::new(); - let mut writer = BitWriter::new(&mut program_and_witness_bytes); - self.encode(&mut writer) - .expect("write to vector never fails"); - debug_assert!(!program_and_witness_bytes.is_empty()); - - program_and_witness_bytes + write_to_vec(|w| self.encode(w)) } }