Решение на Network Packets от Антон Чернев
Резултати
- 20 точки от тестове
- 0 бонус точки
- 20 точки общо
- 15 успешни тест(а)
- 0 неуспешни тест(а)
Код
Лог от изпълнението
Compiling solution v0.1.0 (/tmp/d20200111-2173579-815z2f/solution) Finished test [unoptimized + debuginfo] target(s) in 3.72s Running target/debug/deps/solution-a73e64ec87929bd0 running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out Running target/debug/deps/solution_test-38971695424b36d5 running 15 tests test solution_test::test_construct_packet_from_unicode ... ok test solution_test::test_construct_packet_no_remainder ... ok test solution_test::test_construct_packet_with_remainder ... ok test solution_test::test_construct_packet_with_remainder_cyrillic ... ok test solution_test::test_consuming_packets ... ok test solution_test::test_deserialize_invalid_packet ... ok test solution_test::test_deserialize_packet ... ok test solution_test::test_deserialize_unicode_packet ... ok test solution_test::test_full_roundtrip ... ok test solution_test::test_full_roundtrip_for_zero_size_string ... ok test solution_test::test_invalid_packet_combination ... ok test solution_test::test_iterating_packets ... ok test solution_test::test_iterating_packets_for_zero_size_string ... ok test solution_test::test_serialize_packet ... ok test solution_test::test_zero_size ... ok test result: ok. 15 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out Doc-tests solution running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
История (2 версии и 3 коментара)
Антон качи решение на 03.12.2019 01:11 (преди почти 6 години)
use std::cmp::min;
use std::convert::TryInto;
use std::fmt;
use std::str;
#[derive(PartialEq, Debug)]
pub enum PacketError {
InvalidPacket,
InvalidChecksum,
UnknownProtocolVersion,
CorruptedMessage,
}
impl fmt::Display for PacketError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let message: &str = match self {
PacketError::InvalidPacket => "Invalid Packet",
PacketError::InvalidChecksum => "Invalid Checksum",
PacketError::UnknownProtocolVersion => "Unknown Protocol Version",
PacketError::CorruptedMessage => "Corrupted Message"
};
write!(f, "{}", message)
}
}
impl std::error::Error for PacketError {}
#[derive(PartialEq, Debug)]
pub struct Packet<'a> {
bytes: &'a[u8]
}
impl<'a> Packet<'a> {
const VERSION: u8 = 1;
pub fn from_source(source: &'a[u8], size: u8) -> (Self, &'a[u8]) {
if size <= 0 {
panic!();
}
let length = min(source.len(), size as usize);
let (bytes, rest) = source.split_at(length);
(Self { bytes }, rest)
}
pub fn payload(&self) -> &'a[u8] {
self.bytes
}
pub fn serialize(&self) -> Vec<u8> {
let mut bytes: Vec<u8> = Vec::new();
bytes.push(Self::VERSION);
bytes.push(self.bytes.len() as u8);
let mut checksum: u32 = 0;
for byte in self.bytes {
bytes.push(*byte);
checksum += (*byte) as u32;
};
bytes.extend_from_slice(&checksum.to_be_bytes());
bytes
}
pub fn deserialize(bytes: &'a[u8]) -> Result<(Self, &'a[u8]), PacketError> {
let mut iter = bytes.iter();
match iter.next() {
None => return Err(PacketError::InvalidPacket),
Some(1) => (),
_ => return Err(PacketError::UnknownProtocolVersion)
};
Хитро, харесва ми тестването за присъствие и стойност в един match statement.
let size = match iter.next() {
None => return Err(PacketError::InvalidPacket),
Some(size) => *size as usize
};
let bytes = iter.as_slice();
if bytes.len() < size {
return Err(PacketError::InvalidPacket);
}
let (payload, bytes) = bytes.split_at(size);
let mut checksum: u32 = 0;
for byte in payload {
checksum += *byte as u32;
}
if bytes.len() < 4 {
return Err(PacketError::InvalidPacket)
}
let (checksum_bytes, rest) = bytes.split_at(4);
if u32::from_be_bytes(checksum_bytes.try_into().unwrap()) != checksum {
return Err(PacketError::InvalidChecksum)
}
Ok((Self { bytes: payload }, rest))
}
}
pub struct PacketSerializer<'a> {
packet_size: u8,
bytes: &'a[u8]
}
impl<'a> Iterator for PacketSerializer<'a> {
type Item = Packet<'a>;
fn next(&mut self) -> Option<Self::Item> {
if self.bytes.len() == 0 {
return None;
}
let (packet, rest) = Packet::from_source(self.bytes, self.packet_size);
self.bytes = rest;
Some(packet)
}
}
pub trait Packetable: Sized {
fn to_packets(&self, packet_size: u8) -> PacketSerializer;
fn to_packet_data(&self, packet_size: u8) -> Vec<u8>;
fn from_packet_data(packet_data: &[u8]) -> Result<Self, PacketError>;
}
impl Packetable for String {
fn to_packets(&self, packet_size: u8) -> PacketSerializer {
PacketSerializer { bytes: self.as_bytes(), packet_size }
}
fn to_packet_data(&self, packet_size: u8) -> Vec<u8> {
let mut bytes: Vec<u8> = Vec::new();
for packet in self.to_packets(packet_size) {
bytes.extend(packet.serialize());
}
bytes
}
fn from_packet_data(mut packet_data: &[u8]) -> Result<Self, PacketError> {
- let mut message = String::new();
+ let mut message = Vec::new();
while packet_data.len() > 0 {
let (packet, rest) = Packet::deserialize(packet_data)?;
- message.push_str(match str::from_utf8(packet.payload()) {
- Err(_) => return Err(PacketError::CorruptedMessage),
- Ok(payload) => payload
- });
+ message.extend_from_slice(packet.payload());
packet_data = rest;
}
- Ok(message)
+ match str::from_utf8(&message[..]) {
+ Err(_) => Err(PacketError::CorruptedMessage),
+ Ok(data) => Ok(String::from(data))
+ }
String::from_utf8
също щеше да проработи, при което Ok
ръкава щеше да е Ok(data) => Ok(data)
, така че можеше да го сведеш до
String::from_utf8(&message[..]).map_err(|_| PacketError::CorruptedMessage)
}
}
Добро решение, кратко и четимо.
Хитро, харесва ми тестването за присъствие и стойност в един match statement.
String::from_utf8
също щеше да проработи, при коетоOk
ръкава щеше да еOk(data) => Ok(data)
, така че можеше да го сведеш до