p2p: Change CommandStringDecoder to newtype decoder
What changed, and why it matters
This is a pure code cleanup change. It rewrites a decoder helper from a hand-written struct into one generated by an existing macro, without changing what the code actually does. There is no security-relevant behavior change.
No security action needed. Treat as normal refactoring review.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The commit refactors CommandStringDecoder in p2p/src/message.rs to use the decoder_newtype! macro instead of a manually-implemented struct with an inner ArrayDecoder<12>. The generated code preserves the same push_bytes/end/read_limit semantics, the same UnexpectedEof error mapping, and the same null-trimming plus unchecked UTF-8 conversion in end(). The call site is updated from struct initialization with a named inner field to tuple-struct initialization. No logic, bounds, or validation changes are introduced.
Changed components
p2p/src/message.rsCommandStringDecoderInspect captured patch +11 / −16
diff --git a/p2p/src/message.rs b/p2p/src/message.rs
index 155efeb2..8a2330cd 100644
--- a/p2p/src/message.rs
+++ b/p2p/src/message.rs
@@ -158,22 +158,19 @@ impl encoding::ExactSizeEncoder for CommandStringEncoder {
fn len(&self) -> usize { self.0.len() }
}
-/// Decoder for [`CommandString`].
-#[derive(Debug, Default, Clone)]
-pub struct CommandStringDecoder {
- inner: encoding::ArrayDecoder<12>,
-}
-
-impl encoding::Decoder for CommandStringDecoder {
- type Output = CommandString;
- type Error = CommandStringDecoderError;
+crate::decoder_newtype! {
+ /// Decoder for [`CommandString`].
+ #[derive(Debug, Default, Clone)]
+ pub struct CommandStringDecoder(encoding::ArrayDecoder<12>);
- fn push_bytes(&mut self, bytes: &mut &[u8]) -> Result<encoding::DecoderStatus, Self::Error> {
- self.inner.push_bytes(bytes).map_err(CommandStringDecoderError::UnexpectedEof)
+ fn map_push_bytes_err(err: encoding::UnexpectedEofError) -> CommandStringDecoderError {
+ CommandStringDecoderError::UnexpectedEof(err)
}
- fn end(self) -> Result<Self::Output, Self::Error> {
- let rawbytes = self.inner.end().map_err(CommandStringDecoderError::UnexpectedEof)?;
+ fn end(
+ result: Result<[u8; 12], encoding::UnexpectedEofError>
+ ) -> Result<CommandString, CommandStringDecoderError> {
+ let rawbytes = result.map_err(CommandStringDecoderError::UnexpectedEof)?;
// Trim null padding from the end.
let trimmed =
rawbytes.iter().rposition(|&b| b != 0).map_or(&rawbytes[..0], |i| &rawbytes[..=i]);
@@ -184,8 +181,6 @@ impl encoding::Decoder for CommandStringDecoder {
Ok(CommandString(Cow::Owned(unsafe { String::from_utf8_unchecked(trimmed.to_vec()) })))
}
-
- fn read_limit(&self) -> usize { self.inner.read_limit() }
}
/// A Network message using the v1 p2p protocol.
@@ -1777,7 +1772,7 @@ impl encoding::Decoder for V2NetworkMessageDecoder {
if id == 0 {
// Non-optimized: need to read 12-byte command string next.
self.state = V2NetworkMessageDecoderState::CommandString(
- CommandStringDecoder { inner: encoding::ArrayDecoder::new() },
+ CommandStringDecoder(encoding::ArrayDecoder::new()),
);
} else {
// Optimized short ID (1-28): skip command, go straight to payload.
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.