Skip to content

Commit 33ee39e

Browse files
committed
Optimize length prefix writing
1 parent ff9bc05 commit 33ee39e

File tree

2 files changed

+82
-17
lines changed

2 files changed

+82
-17
lines changed

lightning/src/util/ser.rs

Lines changed: 81 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -63,22 +63,29 @@ impl Writer {
6363
self.0.len()
6464
}
6565

66-
/// Insert the length of a data slice before the slice start
67-
pub fn insert_length(&mut self, slice_start: usize, length_adjust: usize) -> Result<(), ::std::io::Error> {
66+
/// Write the data with a varint length prefix.
67+
/// Use length_adjust to add to the length for special cases, such as writing an Option.
68+
pub fn write_all_with_length<T: Writeable>(&mut self, data: &T, length_adjust: usize) -> Result<(), ::std::io::Error> {
69+
// Most common case is a single byte length prefix, so leave a space for that
70+
self.0.push(0);
71+
let start = self.len();
72+
data.write(self).expect("No in-memory data may fail to serialize");
73+
self.insert_length(start, length_adjust)
74+
}
75+
76+
fn insert_length(&mut self, slice_start: usize, length_adjust: usize) -> Result<(), ::std::io::Error> {
6877
let slice_end = self.len();
6978
let length = slice_end - slice_start + length_adjust;
70-
// Write the length after the slice, to check how many bytes
71-
BigSize(length as u64).write(self)?;
72-
let len_end = self.len();
73-
let len_len = len_end - slice_end;
74-
self.0.resize(slice_end, 0);
75-
// Shift the slice to make space for the length
76-
for _ in 0 .. len_len {
79+
let big_size = BigSize(length as u64);
80+
let len_len = big_size.expected_length();
81+
// At this point we have a placeholder for a varint of length 1 just before the start of data
82+
// Shift the slice to make space for the length. If len_len is 1, no shift is needed.
83+
for _ in 1..len_len {
7784
self.0.insert(slice_start, 0);
7885
}
7986
let mut cursor = Cursor::new(&mut self.0);
80-
cursor.set_position(slice_start as u64);
81-
// Write the length again at the right spot
87+
cursor.set_position((slice_start - 1) as u64);
88+
// Write the length in the space we created before the start of data
8289
BigSize(length as u64).write_write(&mut cursor)
8390
}
8491
}
@@ -264,6 +271,15 @@ impl BigSize {
264271
},
265272
}
266273
}
274+
275+
pub fn expected_length(&self) -> usize {
276+
match self.0 {
277+
0...0xFC => 1,
278+
0xFD...0xFFFF => 3,
279+
0x10000...0xFFFFFFFF => 5,
280+
_ => 9,
281+
}
282+
}
267283
}
268284

269285
impl Writeable for BigSize {
@@ -634,9 +650,7 @@ impl<T: Writeable> Writeable for Option<T> {
634650
match *self {
635651
None => 0u8.write(w)?,
636652
Some(ref data) => {
637-
let start = w.len();
638-
data.write(w).expect("No in-memory data may fail to serialize");
639-
w.insert_length(start, 1)?;
653+
w.write_all_with_length(data, 1)?;
640654
}
641655
}
642656
Ok(())
@@ -756,3 +770,56 @@ impl<A: Writeable, B: Writeable> Writeable for (A, B) {
756770
self.1.write(w)
757771
}
758772
}
773+
774+
#[cfg(test)]
775+
mod tests {
776+
use std::io::Cursor;
777+
use util::ser::*;
778+
779+
#[test]
780+
fn big_size() {
781+
for i in vec![0, 0xfc, 0xfd, 0xffff, 0x10000, 0xFFFFFFFF, 0xFFFFFFFFF] {
782+
let mut writer = Writer::new();
783+
let size = BigSize(i);
784+
size.write(&mut writer).unwrap();
785+
assert_eq!(writer.len(), size.expected_length());
786+
let read_size = BigSize::read(&mut Cursor::new(writer.0)).unwrap();
787+
assert_eq!(read_size.0, size.0);
788+
}
789+
}
790+
791+
#[test]
792+
fn length_prefix_zero() {
793+
impl_array!(0);
794+
let mut writer = Writer::new();
795+
writer.write_all_with_length(&[], 0).unwrap();
796+
// Writing a zero-length length-prefixed item should result in just one zero byte
797+
assert_eq!(writer.0, vec![0]);
798+
}
799+
800+
#[test]
801+
fn length_prefix_one() {
802+
impl_array!(1);
803+
let mut writer = Writer::new();
804+
writer.write_all_with_length(&[0x11u8], 0).unwrap();
805+
assert_eq!(writer.0, vec![1, 0x11]);
806+
}
807+
808+
#[test]
809+
fn length_prefix() {
810+
for size in vec![3, 0xfc, 0xfd, 0xffff, 0x10000] {
811+
let mut writer = Writer::new();
812+
let mut data = Vec::new();
813+
// A Vec serializes its own 2 bytes length, so adjust this
814+
data.resize(size-3, 0x11u8);
815+
// Writing an Option is convenient for round-trip checking of length-prefixed writes
816+
Some(data.clone()).write(&mut writer).unwrap();
817+
let mut reader = Cursor::new(&writer.0);
818+
let read_data: Option<Vec<u8>> = Readable::read(&mut reader).unwrap();
819+
assert_eq!(data, read_data.unwrap());
820+
// Ensure everything was consumed
821+
assert_eq!(reader.position() as usize, writer.len());
822+
println!("{}", writer.len())
823+
}
824+
}
825+
}

lightning/src/util/ser_macros.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,7 @@ macro_rules! encode_tlv {
1212
use util::ser::BigSize;
1313
$(
1414
BigSize($type).write($stream)?;
15-
let start = $stream.len();
16-
$field.write($stream)?;
17-
$stream.insert_length(start, 0)?;
15+
$stream.write_all_with_length(&$field, 0)?;
1816
)*
1917
} }
2018
}

0 commit comments

Comments
 (0)