Skip to content

Commit 4c249f7

Browse files
committed
p2p: add consensus_encoding impls to CommandString
1 parent 6d95f4e commit 4c249f7

File tree

1 file changed

+76
-0
lines changed

1 file changed

+76
-0
lines changed

p2p/src/message.rs

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,82 @@ impl Decodable for CommandString {
134134
}
135135
}
136136

137+
impl encoding::Encodable for CommandString {
138+
type Encoder<'e> = encoding::ArrayEncoder<12>;
139+
140+
fn encoder(&self) -> Self::Encoder<'_> {
141+
let mut rawbytes = [0u8; 12];
142+
let strbytes = self.0.as_bytes();
143+
debug_assert!(strbytes.len() <= 12);
144+
rawbytes[..strbytes.len()].copy_from_slice(strbytes);
145+
encoding::ArrayEncoder::without_length_prefix(rawbytes)
146+
}
147+
}
148+
149+
impl encoding::Decodable for CommandString {
150+
type Decoder = CommandStringDecoder;
151+
152+
fn decoder() -> Self::Decoder { CommandStringDecoder { inner: encoding::ArrayDecoder::new() } }
153+
}
154+
155+
/// Decoder for [`CommandString`].
156+
pub struct CommandStringDecoder {
157+
inner: encoding::ArrayDecoder<12>,
158+
}
159+
160+
impl encoding::Decoder for CommandStringDecoder {
161+
type Output = CommandString;
162+
type Error = CommandStringDecodeError;
163+
164+
fn push_bytes(&mut self, bytes: &mut &[u8]) -> Result<bool, Self::Error> {
165+
self.inner.push_bytes(bytes).map_err(CommandStringDecodeError::UnexpectedEof)
166+
}
167+
168+
fn end(self) -> Result<Self::Output, Self::Error> {
169+
let rawbytes = self.inner.end().map_err(CommandStringDecodeError::UnexpectedEof)?;
170+
// Trim null padding from the end.
171+
let trimmed =
172+
rawbytes.iter().rposition(|&b| b != 0).map_or(&rawbytes[..0], |i| &rawbytes[..=i]);
173+
174+
if !trimmed.is_ascii() {
175+
return Err(CommandStringDecodeError::NotAscii);
176+
}
177+
178+
Ok(CommandString(Cow::Owned(unsafe { String::from_utf8_unchecked(trimmed.to_vec()) })))
179+
}
180+
181+
fn read_limit(&self) -> usize { self.inner.read_limit() }
182+
}
183+
184+
/// Error decoding a [`CommandString`].
185+
#[derive(Debug, Clone, PartialEq, Eq)]
186+
#[non_exhaustive]
187+
pub enum CommandStringDecodeError {
188+
/// Unexpected end of data.
189+
UnexpectedEof(encoding::UnexpectedEofError),
190+
/// Command string contains non-ASCII characters.
191+
NotAscii,
192+
}
193+
194+
impl fmt::Display for CommandStringDecodeError {
195+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
196+
match self {
197+
Self::UnexpectedEof(e) => write!(f, "unexpected end of data: {}", e),
198+
Self::NotAscii => write!(f, "command string must be ASCII"),
199+
}
200+
}
201+
}
202+
203+
#[cfg(feature = "std")]
204+
impl std::error::Error for CommandStringDecodeError {
205+
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
206+
match self {
207+
Self::UnexpectedEof(e) => Some(e),
208+
Self::NotAscii => None,
209+
}
210+
}
211+
}
212+
137213
/// Error returned when a command string is invalid.
138214
///
139215
/// This is currently returned for command strings longer than 12.

0 commit comments

Comments
 (0)