Avoid nested TLV length counting writes
What changed, and why it matters
This commit adds faster, direct ways to calculate how many bytes certain data wrappers will take up when serialized. Previously, these lengths were sometimes computed by actually writing the data to a temporary in-memory buffer and measuring the result. The change avoids that extra work for common wrapper types like references, boxed values, optional values, and length-prefixed collections. It is a performance and code-quality improvement, not a fix for a known security vulnerability.
No security action required. Treat as a normal performance/refactoring commit. Reviewers may optionally verify that each new `serialized_length()` implementation matches the byte count produced by the corresponding `write()` method to ensure consistency.
Security signals we found
Performance optimization only: no input validation, parsing, or cryptographic logic changed
No change to serialized byte format or protocol behavior
No bounds checks, panic handling, or memory safety code modified
No incident, CVE, or security advisory referenced in commit or supplied materials
Evidence from the diff
The patch implements serialized_length() directly on several generic serialization wrappers in lightning/src/util/ser.rs: &T, WithoutLength<S>, Box<T>, and Option<T>. These direct implementations short-circuit the existing TLV length helpers, which previously routed nested payload length calculations through in-memory counting writers. The change reduces allocations and CPU work during serialization length computation, particularly for nested TLV fields. There is no change to wire format, parsing logic, or cryptographic handling.
Changed components
lightning/src/util/ser.rsWriteable trait implementations for &T, Box<T>, Option<T>, WithoutLength<S>TLV serialization length helpersInspect captured patch +26 / −0
diff --git a/lightning/src/util/ser.rs b/lightning/src/util/ser.rs
index 154c5bd..b93be64 100644
--- a/lightning/src/util/ser.rs
+++ b/lightning/src/util/ser.rs
@@ -317,6 +317,11 @@ impl<'a, T: Writeable> Writeable for &'a T {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
(*self).write(writer)
}
+
+ #[inline]
+ fn serialized_length(&self) -> usize {
+ (*self).serialized_length()
+ }
}
/// A trait that various LDK types implement allowing them to be read in from a [`Read`].
@@ -846,6 +851,11 @@ impl<S: AsWriteableSlice> Writeable for WithoutLength<S> {
}
Ok(())
}
+
+ #[inline]
+ fn serialized_length(&self) -> usize {
+ self.0.as_slice().iter().map(|v| v.serialized_length()).sum()
+ }
}
impl<T: MaybeReadable> LengthReadable for WithoutLength<Vec<T>> {
@@ -1366,6 +1376,11 @@ impl<T: Writeable> Writeable for Box<T> {
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
T::write(&**self, w)
}
+
+ #[inline]
+ fn serialized_length(&self) -> usize {
+ T::serialized_length(&**self)
+ }
}
impl<T: Readable> Readable for Box<T> {
@@ -1385,6 +1400,17 @@ impl<T: Writeable> Writeable for Option<T> {
}
Ok(())
}
+
+ #[inline]
+ fn serialized_length(&self) -> usize {
+ match *self {
+ None => 1,
+ Some(ref data) => {
+ let data_len = data.serialized_length();
+ BigSize(data_len as u64 + 1).serialized_length() + data_len
+ },
+ }
+ }
}
impl<T: LengthReadable> Readable for Option<T> {
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.