Решение на Network Packets от Андрей

Обратно към всички решения

Към профила на Андрей

Резултати

  • 20 точки от тестове
  • 0 бонус точки
  • 20 точки общо
  • 15 успешни тест(а)
  • 0 неуспешни тест(а)

Код

use std::fmt;
use std::write;
use std::convert::TryInto;
#[derive(Debug)]
pub enum PacketError {
InvalidPacket,
InvalidChecksum,
UnknownProtocolVersion,
CorruptedMessage,
}
impl fmt::Display for PacketError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
PacketError::InvalidPacket => write!(f, "Invalid packet"),
PacketError::InvalidChecksum => write!(f, "Checksum does not match"),
PacketError::UnknownProtocolVersion => write!(f, "Unknown protocol version"),
PacketError::CorruptedMessage => write!(f, "Couldn't combine packets into UTF8 String"),
}
}
}
impl std::error::Error for PacketError {}
#[derive(PartialEq, Debug)]
pub struct Packet<'a> {
payload: &'a [u8],
size: u8,
checksum: u32,
}
impl<'a> Packet<'a> {
pub fn from_source(source: &'a [u8], size: u8) -> (Self, &'a [u8]) {
let size = (size as usize).min(source.len());
if size == 0 {
panic!("Size can't be zero");
}
let (payload, remainder) = source.split_at(size);
let checksum = payload.iter().map(|n| *n as u32).sum();
let size = size as u8;
(Packet { payload, size, checksum }, remainder)
}
pub fn payload(&self) -> &[u8] {
self.payload
}
pub fn serialize(&self) -> Vec<u8> {
let mut data = Vec::new();
data.push(1); // version
data.push(self.size);
data.extend_from_slice(&self.payload);
data.extend_from_slice(&self.checksum.to_be_bytes());
data
}
// Note: if size is less, it'll be the checksum not being valid
pub fn deserialize(bytes: &[u8]) -> Result<(Packet, &[u8]), PacketError> {
let (&version, bytes) = bytes.split_first().ok_or_else(|| PacketError::InvalidPacket)?;
if version != 1 {
return Err(PacketError::UnknownProtocolVersion);
}
let (&size, bytes) = bytes.split_first().ok_or_else(|| PacketError::InvalidPacket)?;
if bytes.len() < (size as usize) + 4 {
return Err(PacketError::InvalidPacket);
}
let (payload, bytes) = bytes.split_at(size as usize);
let (checksum_bytes, bytes) = bytes.split_at(4);
let calculated_checksum: u32 = payload.iter().map(|n| *n as u32).sum();
let checksum = u32::from_be_bytes(checksum_bytes.try_into().map_err(|_| PacketError::InvalidPacket)?);
if calculated_checksum != checksum {
return Err(PacketError::InvalidChecksum);
}
Ok((Packet { size, payload, checksum }, bytes))
}
}
pub struct PacketSerializer<'a> {
source: &'a [u8],
packet_size: u8,
}
impl<'a> Iterator for PacketSerializer<'a> {
type Item = Packet<'a>;
fn next(&mut self) -> Option<Self::Item> {
if self.source.len() == 0 {
return None;
}
let (packet, remainder) = Packet::from_source(self.source, self.packet_size);
self.source = remainder;
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 { source: self.as_bytes(), packet_size }
}
fn to_packet_data(&self, packet_size: u8) -> Vec<u8> {
self.to_packets(packet_size).into_iter().flat_map(|packet| packet.serialize()).collect()
}
fn from_packet_data(packet_data: &[u8]) -> Result<Self, PacketError> {
let mut bytes = Vec::new();
let mut packet_data = packet_data;
while packet_data.len() > 0 {
let (packet, remainder) = Packet::deserialize(packet_data)?;
packet_data = remainder;
bytes.extend_from_slice(packet.payload);
}
String::from_utf8(bytes).map_err(|_| PacketError::CorruptedMessage)
}
}

Лог от изпълнението

Compiling solution v0.1.0 (/tmp/d20200111-2173579-o6aszn/solution)
    Finished test [unoptimized + debuginfo] target(s) in 3.71s
     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 версии и 0 коментара)

Андрей качи първо решение на 26.11.2019 16:08 (преди почти 6 години)

Андрей качи решение на 03.12.2019 10:31 (преди почти 6 години)

use std::fmt;
use std::write;
use std::convert::TryInto;
#[derive(Debug)]
pub enum PacketError {
InvalidPacket,
InvalidChecksum,
UnknownProtocolVersion,
CorruptedMessage,
}
impl fmt::Display for PacketError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
PacketError::InvalidPacket => write!(f, "Invalid packet"),
PacketError::InvalidChecksum => write!(f, "Checksum does not match"),
PacketError::UnknownProtocolVersion => write!(f, "Unknown protocol version"),
PacketError::CorruptedMessage => write!(f, "Couldn't combine packets into UTF8 String"),
}
}
}
impl std::error::Error for PacketError {}
#[derive(PartialEq, Debug)]
pub struct Packet<'a> {
payload: &'a [u8],
size: u8,
checksum: u32,
}
impl<'a> Packet<'a> {
pub fn from_source(source: &'a [u8], size: u8) -> (Self, &'a [u8]) {
+ let size = (size as usize).min(source.len());
+
if size == 0 {
panic!("Size can't be zero");
}
- let size = (size as usize).min(source.len());
let (payload, remainder) = source.split_at(size);
let checksum = payload.iter().map(|n| *n as u32).sum();
let size = size as u8;
(Packet { payload, size, checksum }, remainder)
}
pub fn payload(&self) -> &[u8] {
self.payload
}
pub fn serialize(&self) -> Vec<u8> {
let mut data = Vec::new();
data.push(1); // version
data.push(self.size);
data.extend_from_slice(&self.payload);
data.extend_from_slice(&self.checksum.to_be_bytes());
data
}
// Note: if size is less, it'll be the checksum not being valid
pub fn deserialize(bytes: &[u8]) -> Result<(Packet, &[u8]), PacketError> {
let (&version, bytes) = bytes.split_first().ok_or_else(|| PacketError::InvalidPacket)?;
if version != 1 {
return Err(PacketError::UnknownProtocolVersion);
}
let (&size, bytes) = bytes.split_first().ok_or_else(|| PacketError::InvalidPacket)?;
if bytes.len() < (size as usize) + 4 {
return Err(PacketError::InvalidPacket);
}
let (payload, bytes) = bytes.split_at(size as usize);
let (checksum_bytes, bytes) = bytes.split_at(4);
let calculated_checksum: u32 = payload.iter().map(|n| *n as u32).sum();
let checksum = u32::from_be_bytes(checksum_bytes.try_into().map_err(|_| PacketError::InvalidPacket)?);
if calculated_checksum != checksum {
return Err(PacketError::InvalidChecksum);
}
Ok((Packet { size, payload, checksum }, bytes))
}
}
pub struct PacketSerializer<'a> {
source: &'a [u8],
packet_size: u8,
}
impl<'a> Iterator for PacketSerializer<'a> {
type Item = Packet<'a>;
fn next(&mut self) -> Option<Self::Item> {
if self.source.len() == 0 {
return None;
}
let (packet, remainder) = Packet::from_source(self.source, self.packet_size);
self.source = remainder;
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 { source: self.as_bytes(), packet_size }
}
fn to_packet_data(&self, packet_size: u8) -> Vec<u8> {
self.to_packets(packet_size).into_iter().flat_map(|packet| packet.serialize()).collect()
}
fn from_packet_data(packet_data: &[u8]) -> Result<Self, PacketError> {
let mut bytes = Vec::new();
let mut packet_data = packet_data;
while packet_data.len() > 0 {
let (packet, remainder) = Packet::deserialize(packet_data)?;
packet_data = remainder;
bytes.extend_from_slice(packet.payload);
}
String::from_utf8(bytes).map_err(|_| PacketError::CorruptedMessage)
}
}