Решение на Network Packets от Илиян Драгнев
Резултати
- 19 точки от тестове
- 1 бонус точка
- 20 точки общо
- 14 успешни тест(а)
- 1 неуспешни тест(а)
Код
Лог от изпълнението
Compiling solution v0.1.0 (/tmp/d20200111-2173579-a64xo7/solution) Finished test [unoptimized + debuginfo] target(s) in 4.76s Running target/debug/deps/solution-a73e64ec87929bd0 running 12 tests test tests::deserialize_with_invalid_version_returns_unknown_version_err_no_matter_the_size ... ok test tests::deserialize_with_no_size_returns_invalid_packet_error ... ok test tests::deserialize_with_no_version_returns_invalid_packet_error ... ok test tests::deserialize_with_single_valid_packet ... ok test tests::deserialize_with_size_0_returns_invalid_packet_error ... ok test tests::from_source_exact_size ... ok test tests::from_source_greater_size ... ok test tests::from_source_panics_on_size_0 ... ok test tests::from_source_smaller_size ... ok test tests::serialize ... ok test tests::string_from_packet_data_and_to_packet_data_compose_to_identity ... ok test tests::string_to_packets_produces_a_valid_range ... ok test result: ok. 12 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 ... FAILED 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 failures: ---- solution_test::test_deserialize_invalid_packet stdout ---- thread 'main' panicked at 'index 4 out of range for slice of length 0', src/libcore/slice/mod.rs:2664:5 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace. failures: solution_test::test_deserialize_invalid_packet test result: FAILED. 14 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out error: test failed, to rerun pass '--test solution_test'
История (7 версии и 3 коментара)
Илиян качи решение на 28.11.2019 17:01 (преди почти 6 години)
use std::fmt;
#[derive(Debug, Copy, Clone)]
pub enum PacketError {
InvalidPacket,
InvalidChecksum,
UnknownProtocolVersion,
CorruptedMessage,
}
impl fmt::Display for PacketError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let error = match self {
PacketError::InvalidPacket => "Invalid packet",
PacketError::InvalidChecksum => "Invalid checksum",
PacketError::CorruptedMessage => "Corrupted message",
PacketError::UnknownProtocolVersion => "Unknown protocol version",
};
write!(f, "Packet error: {}", error)
}
}
impl std::error::Error for PacketError { }
#[derive(PartialEq, Debug)]
pub struct Packet<'a> {
version: u8,
size: u8,
payload: &'a [u8],
checksum: u32,
}
impl<'a> Packet<'a> {
pub fn from_source(source: &'a [u8], size: u8) -> (Packet<'a>, &'a [u8]) {
if size == 0 {
panic!("size 0 passed to Packet::from_source");
}
let size = std::cmp::max(source.len(), size as usize);
let payload = &source[..size];
let rest = &source[size..];
let checksum = payload.iter().sum::<u8>() as u32;
let packet = Packet {
version: 1,
size: size as u8, //reconsider
payload,
checksum,
};
(packet, rest)
}
pub fn payload(&self) -> &[u8] {
self.payload
}
pub fn serialize(&self) -> Vec<u8> {
[self.version, self.size]
.iter()
.chain(self.payload)
.chain(&self.checksum.to_be_bytes())
.copied()
.collect()
}
- /// Ако липсват версия, размер, чексума, или размера е твърде малък, за да може да се изпарси
- /// валиден пакет от байтовете, връщаме грешка `PacketError::InvalidPacket`.
- ///
- /// Ако версията е различна от 1, връщаме `PacketError::UnknownProtocolVersion`.
- ///
- /// Ако checksum-а, който прочитаме от последните 4 байта на пакета е различен от изчисления
- /// checksum на payload-а (сумата от байтовете му), връщаме `PacketError::InvalidChecksum`.
- ///
- /// Забележете, че ако размера е по-голям от истинския размер на payload-а, се очаква
- /// `PacketError::InvalidPacket`. Ако размера е по-малък от истинския размер на payload-а,
- /// въпросния ще се изпарси, но чексумата ще е грешна, така че ще очакваме
- /// `PacketError::InvalidChecksum`. Малко тъпо! Но уви, протоколите имат подобни тъпи ръбове,
- /// особено като са написани за един уикенд. Авторите обещават по-добър протокол за версия 2.
- ///
pub fn deserialize(bytes: &[u8]) -> Result<(Packet, &[u8]), PacketError> {
let mut iter = bytes.iter();
let (version, size) = (iter.next(), iter.next());
- match (version, size) {
- (None, _) => Err(PacketError::InvalidPacket),
+ match (version, size) { //TODO: verify the cases and the errors
+ (None, _) => Err(PacketError::InvalidPacket),
(_, None) => Err(PacketError::InvalidPacket),
(Some(1), Some(s)) => Packet::try_from_source(&bytes[2..], *s),
(Some(_), _) => Err(PacketError::UnknownProtocolVersion),
}
}
fn try_from_source(bytes: &[u8], size: u8) -> Result<(Packet, &[u8]), PacketError> {
use std::convert::TryInto;
let (packet, rest) = Packet::from_source(bytes, size);
let checksum_end = 4;
- let checksum_as_bytes = &rest[..checksum_end].try_into().unwrap(); //reconsider
+ let checksum_as_bytes = &rest[..checksum_end]
+ .try_into()
+ .map_err(|_| PacketError::InvalidPacket)?;
let checksum = u32::from_be_bytes(*checksum_as_bytes);
if packet.checksum != checksum {
Err(PacketError::InvalidChecksum)
}
else {
Ok((packet, &bytes[checksum_end..]))
}
}
}
pub struct PacketSerializer<'a> {
bytes: &'a [u8],
+ packet_size: u8,
}
+impl<'a> PacketSerializer<'a> {
+ pub fn from(bytes: &'a [u8], packet_size: u8) -> PacketSerializer<'a> {
+ PacketSerializer {
+ bytes,
+ packet_size,
+ }
+ }
+}
+
impl<'a> Iterator for PacketSerializer<'a> {
type Item = Packet<'a>;
fn next(&mut self) -> Option<Self::Item> {
- if let Ok((packet, bytes)) = Packet::deserialize(&self.bytes) {
+ if self.bytes.len() > 0 {
+ let (packet, bytes) = Packet::from_source(&self.bytes, self.packet_size);
self.bytes = bytes;
Some(packet)
- }
+ }
else {
None
}
}
}
-/// Този trait ще ни позволи да конвертираме един `String` (а ако искаме, и други неща) от и до
-/// комплект от байтове за прехвърляне по мрежата.
-///
-/// Детайли за методите вижте по-долу в имплементацията на този trait за `String`.
-///
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 {
- /// Този метод приема размер, който да използваме за размера на payload-а на всеки пакет. Връща
- /// итератор върху въпросните пакети. Низа трябва да се използва под формата на байтове.
- ///
fn to_packets(&self, packet_size: u8) -> PacketSerializer {
- unimplemented!()
+ PacketSerializer{
+ bytes: self.as_bytes(),
+ packet_size,
+ }
}
- /// Имайки итератор по пакети, лесно можем да сериализираме всеки индивидуален пакет в поредица
- /// от байтове със `.serialize()` и да го натъпчем във вектора.
- ///
fn to_packet_data(&self, packet_size: u8) -> Vec<u8> {
- unimplemented!()
+ self
+ .to_packets(packet_size)
+ .flat_map(|p| p.serialize())
+ .collect()
}
- /// Обратното на горния метод е тази асоциирана функция -- имайки slice от байтове които са
- /// сериализирана репрезентация на пакети, искаме да десериализираме пакети от този slice, да
- /// им извадим payload-ите, и да ги сглобим в оригиналното съобщение.
- ///
- /// Грешките, които могат да се върнат, са същите, които идват от `.deserialize()`.
- ///
- /// Една допълнителна грешка, която може да се случи е при сглобяване на съобщението -- ако е
- /// имало липсващ пакет, може съчетанието на байтовете да не генерира правилно UTF8 съобщение.
- /// Тогава връщаме `PacketError::CorruptedMessage`.
- ///
- fn from_packet_data(packet_data: &[u8]) -> Result<Self, PacketError> {
- unimplemented!()
+ fn from_packet_data(mut packet_data: &[u8]) -> Result<Self, PacketError> {
+ let mut bytes: Vec<u8> = Vec::new();
+ while packet_data.len() > 0 {
+ let (packet, rest) = Packet::deserialize(packet_data)?;
+ bytes.extend_from_slice(packet.payload());
+ packet_data = rest;
+ }
+
+ String::from_utf8(bytes)
+ .map_err(|_| PacketError::CorruptedMessage)
}
}
Илиян качи решение на 29.11.2019 18:45 (преди почти 6 години)
use std::fmt;
#[derive(Debug, Copy, Clone)]
pub enum PacketError {
InvalidPacket,
InvalidChecksum,
UnknownProtocolVersion,
CorruptedMessage,
}
impl fmt::Display for PacketError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let error = match self {
PacketError::InvalidPacket => "Invalid packet",
PacketError::InvalidChecksum => "Invalid checksum",
PacketError::CorruptedMessage => "Corrupted message",
PacketError::UnknownProtocolVersion => "Unknown protocol version",
};
write!(f, "Packet error: {}", error)
}
}
impl std::error::Error for PacketError { }
#[derive(PartialEq, Debug)]
pub struct Packet<'a> {
version: u8,
size: u8,
payload: &'a [u8],
checksum: u32,
}
impl<'a> Packet<'a> {
pub fn from_source(source: &'a [u8], size: u8) -> (Packet<'a>, &'a [u8]) {
if size == 0 {
panic!("size 0 passed to Packet::from_source");
}
- let size = std::cmp::max(source.len(), size as usize);
+ use std::cmp::min;
+
+ let size = min(source.len(), size as usize);
let payload = &source[..size];
let rest = &source[size..];
- let checksum = payload.iter().sum::<u8>() as u32;
+ let checksum = payload.iter().fold(0, |acc, x| acc + *x as u32);
let packet = Packet {
version: 1,
- size: size as u8, //reconsider
+ size: size as u8,
payload,
checksum,
};
(packet, rest)
}
pub fn payload(&self) -> &[u8] {
self.payload
}
pub fn serialize(&self) -> Vec<u8> {
[self.version, self.size]
.iter()
.chain(self.payload)
.chain(&self.checksum.to_be_bytes())
.copied()
.collect()
}
+ /// Ако размера е твърде малък, за да може да се изпарси
+ /// валиден пакет от байтовете, връщаме грешка `PacketError::InvalidPacket`.
+ ///
+ /// Ако версията е различна от 1, връщаме `PacketError::UnknownProtocolVersion`.
+ ///
+ /// Ако checksum-а, който прочитаме от последните 4 байта на пакета е различен от изчисления
+ /// checksum на payload-а (сумата от байтовете му), връщаме `PacketError::InvalidChecksum`.
+ ///
+ /// Забележете, че ако размера е по-голям от истинския размер на payload-а, се очаква
+ /// `PacketError::InvalidPacket`. Ако размера е по-малък от истинския размер на payload-а,
+ /// въпросния ще се изпарси, но чексумата ще е грешна, така че ще очакваме
+ /// `PacketError::InvalidChecksum`. Малко тъпо! Но уви, протоколите имат подобни тъпи ръбове,
+ /// особено като са написани за един уикенд. Авторите обещават по-добър протокол за версия 2.
+ ///
pub fn deserialize(bytes: &[u8]) -> Result<(Packet, &[u8]), PacketError> {
let mut iter = bytes.iter();
let (version, size) = (iter.next(), iter.next());
- match (version, size) { //TODO: verify the cases and the errors
+ match (version, size) {
(None, _) => Err(PacketError::InvalidPacket),
(_, None) => Err(PacketError::InvalidPacket),
- (Some(1), Some(s)) => Packet::try_from_source(&bytes[2..], *s),
+ (Some(1), Some(0)) => Err(PacketError::InvalidPacket),
+ (Some(1), Some(s)) => Packet::deserizalize_from_source(&bytes[2..], *s),
(Some(_), _) => Err(PacketError::UnknownProtocolVersion),
}
}
- fn try_from_source(bytes: &[u8], size: u8) -> Result<(Packet, &[u8]), PacketError> {
+ fn deserizalize_from_source(bytes: &[u8], size: u8) -> Result<(Packet, &[u8]), PacketError> {
use std::convert::TryInto;
let (packet, rest) = Packet::from_source(bytes, size);
let checksum_end = 4;
let checksum_as_bytes = &rest[..checksum_end]
.try_into()
.map_err(|_| PacketError::InvalidPacket)?;
let checksum = u32::from_be_bytes(*checksum_as_bytes);
if packet.checksum != checksum {
Err(PacketError::InvalidChecksum)
}
else {
Ok((packet, &bytes[checksum_end..]))
}
}
}
pub struct PacketSerializer<'a> {
bytes: &'a [u8],
packet_size: u8,
}
impl<'a> PacketSerializer<'a> {
pub fn from(bytes: &'a [u8], packet_size: u8) -> PacketSerializer<'a> {
PacketSerializer {
bytes,
packet_size,
}
}
}
impl<'a> Iterator for PacketSerializer<'a> {
type Item = Packet<'a>;
fn next(&mut self) -> Option<Self::Item> {
if self.bytes.len() > 0 {
let (packet, bytes) = Packet::from_source(&self.bytes, self.packet_size);
self.bytes = bytes;
Some(packet)
}
else {
None
}
}
}
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{
+ PacketSerializer {
bytes: self.as_bytes(),
packet_size,
}
}
fn to_packet_data(&self, packet_size: u8) -> Vec<u8> {
self
.to_packets(packet_size)
.flat_map(|p| p.serialize())
.collect()
}
fn from_packet_data(mut packet_data: &[u8]) -> Result<Self, PacketError> {
let mut bytes: Vec<u8> = Vec::new();
while packet_data.len() > 0 {
let (packet, rest) = Packet::deserialize(packet_data)?;
bytes.extend_from_slice(packet.payload());
packet_data = rest;
}
String::from_utf8(bytes)
.map_err(|_| PacketError::CorruptedMessage)
}
}
Илиян качи решение на 29.11.2019 18:46 (преди почти 6 години)
use std::fmt;
#[derive(Debug, Copy, Clone)]
pub enum PacketError {
InvalidPacket,
InvalidChecksum,
UnknownProtocolVersion,
CorruptedMessage,
}
impl fmt::Display for PacketError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let error = match self {
PacketError::InvalidPacket => "Invalid packet",
PacketError::InvalidChecksum => "Invalid checksum",
PacketError::CorruptedMessage => "Corrupted message",
PacketError::UnknownProtocolVersion => "Unknown protocol version",
};
write!(f, "Packet error: {}", error)
}
}
impl std::error::Error for PacketError { }
#[derive(PartialEq, Debug)]
pub struct Packet<'a> {
version: u8,
size: u8,
payload: &'a [u8],
checksum: u32,
}
impl<'a> Packet<'a> {
pub fn from_source(source: &'a [u8], size: u8) -> (Packet<'a>, &'a [u8]) {
if size == 0 {
panic!("size 0 passed to Packet::from_source");
}
use std::cmp::min;
let size = min(source.len(), size as usize);
let payload = &source[..size];
let rest = &source[size..];
let checksum = payload.iter().fold(0, |acc, x| acc + *x as u32);
let packet = Packet {
version: 1,
size: size as u8,
payload,
checksum,
};
(packet, rest)
}
pub fn payload(&self) -> &[u8] {
self.payload
}
pub fn serialize(&self) -> Vec<u8> {
[self.version, self.size]
.iter()
.chain(self.payload)
.chain(&self.checksum.to_be_bytes())
.copied()
.collect()
}
- /// Ако размера е твърде малък, за да може да се изпарси
- /// валиден пакет от байтовете, връщаме грешка `PacketError::InvalidPacket`.
- ///
- /// Ако версията е различна от 1, връщаме `PacketError::UnknownProtocolVersion`.
- ///
- /// Ако checksum-а, който прочитаме от последните 4 байта на пакета е различен от изчисления
- /// checksum на payload-а (сумата от байтовете му), връщаме `PacketError::InvalidChecksum`.
- ///
- /// Забележете, че ако размера е по-голям от истинския размер на payload-а, се очаква
- /// `PacketError::InvalidPacket`. Ако размера е по-малък от истинския размер на payload-а,
- /// въпросния ще се изпарси, но чексумата ще е грешна, така че ще очакваме
- /// `PacketError::InvalidChecksum`. Малко тъпо! Но уви, протоколите имат подобни тъпи ръбове,
- /// особено като са написани за един уикенд. Авторите обещават по-добър протокол за версия 2.
- ///
pub fn deserialize(bytes: &[u8]) -> Result<(Packet, &[u8]), PacketError> {
let mut iter = bytes.iter();
let (version, size) = (iter.next(), iter.next());
match (version, size) {
(None, _) => Err(PacketError::InvalidPacket),
(_, None) => Err(PacketError::InvalidPacket),
(Some(1), Some(0)) => Err(PacketError::InvalidPacket),
(Some(1), Some(s)) => Packet::deserizalize_from_source(&bytes[2..], *s),
(Some(_), _) => Err(PacketError::UnknownProtocolVersion),
}
}
fn deserizalize_from_source(bytes: &[u8], size: u8) -> Result<(Packet, &[u8]), PacketError> {
use std::convert::TryInto;
let (packet, rest) = Packet::from_source(bytes, size);
let checksum_end = 4;
let checksum_as_bytes = &rest[..checksum_end]
.try_into()
.map_err(|_| PacketError::InvalidPacket)?;
let checksum = u32::from_be_bytes(*checksum_as_bytes);
if packet.checksum != checksum {
Err(PacketError::InvalidChecksum)
}
else {
Ok((packet, &bytes[checksum_end..]))
}
}
}
pub struct PacketSerializer<'a> {
bytes: &'a [u8],
packet_size: u8,
}
impl<'a> PacketSerializer<'a> {
pub fn from(bytes: &'a [u8], packet_size: u8) -> PacketSerializer<'a> {
PacketSerializer {
bytes,
packet_size,
}
}
}
impl<'a> Iterator for PacketSerializer<'a> {
type Item = Packet<'a>;
fn next(&mut self) -> Option<Self::Item> {
if self.bytes.len() > 0 {
let (packet, bytes) = Packet::from_source(&self.bytes, self.packet_size);
self.bytes = bytes;
Some(packet)
}
else {
None
}
}
}
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> {
self
.to_packets(packet_size)
.flat_map(|p| p.serialize())
.collect()
}
fn from_packet_data(mut packet_data: &[u8]) -> Result<Self, PacketError> {
let mut bytes: Vec<u8> = Vec::new();
while packet_data.len() > 0 {
let (packet, rest) = Packet::deserialize(packet_data)?;
bytes.extend_from_slice(packet.payload());
packet_data = rest;
}
String::from_utf8(bytes)
.map_err(|_| PacketError::CorruptedMessage)
}
}
Илиян качи решение на 29.11.2019 23:03 (преди почти 6 години)
use std::fmt;
#[derive(Debug, Copy, Clone)]
pub enum PacketError {
InvalidPacket,
InvalidChecksum,
UnknownProtocolVersion,
CorruptedMessage,
}
impl fmt::Display for PacketError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let error = match self {
PacketError::InvalidPacket => "Invalid packet",
PacketError::InvalidChecksum => "Invalid checksum",
PacketError::CorruptedMessage => "Corrupted message",
PacketError::UnknownProtocolVersion => "Unknown protocol version",
};
write!(f, "Packet error: {}", error)
}
}
impl std::error::Error for PacketError { }
#[derive(PartialEq, Debug)]
pub struct Packet<'a> {
version: u8,
size: u8,
payload: &'a [u8],
checksum: u32,
}
impl<'a> Packet<'a> {
pub fn from_source(source: &'a [u8], size: u8) -> (Packet<'a>, &'a [u8]) {
if size == 0 {
panic!("size 0 passed to Packet::from_source");
}
use std::cmp::min;
let size = min(source.len(), size as usize);
let payload = &source[..size];
let rest = &source[size..];
let checksum = payload.iter().fold(0, |acc, x| acc + *x as u32);
let packet = Packet {
version: 1,
size: size as u8,
payload,
checksum,
};
(packet, rest)
}
pub fn payload(&self) -> &[u8] {
self.payload
}
+
+ pub fn size(&self) -> u8 {
+ self.size
+ }
+
+ pub fn checksum(&self) -> u32 {
+ self.checksum
+ }
+ pub fn version(&self) -> u8 {
+ self.version
+ }
+
pub fn serialize(&self) -> Vec<u8> {
[self.version, self.size]
.iter()
.chain(self.payload)
.chain(&self.checksum.to_be_bytes())
.copied()
.collect()
}
-
+
pub fn deserialize(bytes: &[u8]) -> Result<(Packet, &[u8]), PacketError> {
let mut iter = bytes.iter();
let (version, size) = (iter.next(), iter.next());
match (version, size) {
(None, _) => Err(PacketError::InvalidPacket),
(_, None) => Err(PacketError::InvalidPacket),
(Some(1), Some(0)) => Err(PacketError::InvalidPacket),
(Some(1), Some(s)) => Packet::deserizalize_from_source(&bytes[2..], *s),
(Some(_), _) => Err(PacketError::UnknownProtocolVersion),
}
}
fn deserizalize_from_source(bytes: &[u8], size: u8) -> Result<(Packet, &[u8]), PacketError> {
use std::convert::TryInto;
let (packet, rest) = Packet::from_source(bytes, size);
let checksum_end = 4;
let checksum_as_bytes = &rest[..checksum_end]
.try_into()
.map_err(|_| PacketError::InvalidPacket)?;
let checksum = u32::from_be_bytes(*checksum_as_bytes);
if packet.checksum != checksum {
Err(PacketError::InvalidChecksum)
}
else {
Ok((packet, &bytes[checksum_end..]))
}
}
}
pub struct PacketSerializer<'a> {
bytes: &'a [u8],
packet_size: u8,
}
impl<'a> PacketSerializer<'a> {
pub fn from(bytes: &'a [u8], packet_size: u8) -> PacketSerializer<'a> {
PacketSerializer {
bytes,
packet_size,
}
}
}
impl<'a> Iterator for PacketSerializer<'a> {
type Item = Packet<'a>;
fn next(&mut self) -> Option<Self::Item> {
if self.bytes.len() > 0 {
let (packet, bytes) = Packet::from_source(&self.bytes, self.packet_size);
self.bytes = bytes;
Some(packet)
}
else {
None
}
}
}
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> {
self
.to_packets(packet_size)
.flat_map(|p| p.serialize())
.collect()
}
fn from_packet_data(mut packet_data: &[u8]) -> Result<Self, PacketError> {
let mut bytes: Vec<u8> = Vec::new();
while packet_data.len() > 0 {
let (packet, rest) = Packet::deserialize(packet_data)?;
bytes.extend_from_slice(packet.payload());
packet_data = rest;
}
String::from_utf8(bytes)
.map_err(|_| PacketError::CorruptedMessage)
+ }
+}
+
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ #[should_panic]
+ fn from_source_panics_on_size_0() {
+ let _ = Packet::from_source(b"abc", 0);
+ }
+
+ #[test]
+ fn from_source_exact_size() {
+ let source = b"abcd";
+ let packet_size = source.len() as u8;
+
+ let (packet, rest) = Packet::from_source(source, packet_size);
+
+ assert!(rest.is_empty());
+ assert_eq!(source, packet.payload());
+ }
+
+ #[test]
+ fn from_source_greater_size() {
+ let source = b"abcd";
+ let packet_size = source.len() as u8;
+
+ let (packet, rest) = Packet::from_source(source, packet_size + 1);
+
+ assert!(rest.is_empty());
+ assert_eq!(source, packet.payload());
+ }
+
+ #[test]
+ fn from_source_smaller_size() {
+ let source = b"abcd";
+ let packet_size = source.len() as u8;
+
+ let (packet, rest) = Packet::from_source(source, packet_size - 1);
+
+ assert_eq!(packet.payload(), b"abc");
+ assert_eq!(rest, b"d");
+ }
+
+ #[test]
+ fn serialize() {
+ use std::convert::TryInto;
+
+ let source = b"abcd";
+ let header_size = 2;
+ let checksum_size = 4;
+ let (packet, _) = Packet::from_source(source, source.len() as u8);
+ let checksum_start = (header_size + packet.size()) as usize;
+
+ let bytes = packet.serialize();
+ let checksum_bytes = &bytes[checksum_start..].try_into().unwrap();
+
+ assert_eq!(bytes.len() as u8, packet.size() + header_size + checksum_size);
+ assert_eq!(bytes[0], packet.version());
+ assert_eq!(bytes[1], packet.size());
+ assert_eq!(&bytes[2..checksum_start], packet.payload());
+ assert_eq!(u32::from_be_bytes(*checksum_bytes), packet.checksum());
}
}
Илиян качи решение на 01.12.2019 23:28 (преди почти 6 години)
use std::fmt;
-#[derive(Debug, Copy, Clone)]
+#[derive(Debug, Copy, Clone, PartialEq)]
pub enum PacketError {
InvalidPacket,
InvalidChecksum,
UnknownProtocolVersion,
CorruptedMessage,
}
impl fmt::Display for PacketError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let error = match self {
PacketError::InvalidPacket => "Invalid packet",
PacketError::InvalidChecksum => "Invalid checksum",
PacketError::CorruptedMessage => "Corrupted message",
PacketError::UnknownProtocolVersion => "Unknown protocol version",
};
write!(f, "Packet error: {}", error)
}
}
impl std::error::Error for PacketError { }
#[derive(PartialEq, Debug)]
pub struct Packet<'a> {
version: u8,
size: u8,
payload: &'a [u8],
checksum: u32,
}
impl<'a> Packet<'a> {
pub fn from_source(source: &'a [u8], size: u8) -> (Packet<'a>, &'a [u8]) {
if size == 0 {
panic!("size 0 passed to Packet::from_source");
}
use std::cmp::min;
let size = min(source.len(), size as usize);
let payload = &source[..size];
let rest = &source[size..];
let checksum = payload.iter().fold(0, |acc, x| acc + *x as u32);
let packet = Packet {
version: 1,
size: size as u8,
payload,
checksum,
};
(packet, rest)
}
- pub fn payload(&self) -> &[u8] {
- self.payload
- }
-
- pub fn size(&self) -> u8 {
- self.size
- }
-
- pub fn checksum(&self) -> u32 {
- self.checksum
- }
-
- pub fn version(&self) -> u8 {
- self.version
- }
-
pub fn serialize(&self) -> Vec<u8> {
[self.version, self.size]
.iter()
.chain(self.payload)
.chain(&self.checksum.to_be_bytes())
.copied()
.collect()
}
-
+
pub fn deserialize(bytes: &[u8]) -> Result<(Packet, &[u8]), PacketError> {
let mut iter = bytes.iter();
- let (version, size) = (iter.next(), iter.next());
+ let version = iter.next();
+ let size = iter.next();
match (version, size) {
- (None, _) => Err(PacketError::InvalidPacket),
- (_, None) => Err(PacketError::InvalidPacket),
- (Some(1), Some(0)) => Err(PacketError::InvalidPacket),
- (Some(1), Some(s)) => Packet::deserizalize_from_source(&bytes[2..], *s),
- (Some(_), _) => Err(PacketError::UnknownProtocolVersion),
+ (None, _) => Err(PacketError::InvalidPacket),
+ (_, None) => Err(PacketError::InvalidPacket),
+ (Some(1), Some(0)) => Err(PacketError::InvalidPacket),
+ (Some(1), Some(s)) => Packet::deserizalize_from_source(&bytes[2..], *s),
+ (Some(_), _) => Err(PacketError::UnknownProtocolVersion),
}
}
-
+
fn deserizalize_from_source(bytes: &[u8], size: u8) -> Result<(Packet, &[u8]), PacketError> {
use std::convert::TryInto;
- let (packet, rest) = Packet::from_source(bytes, size);
+ let (packet, bytes) = Packet::from_source(bytes, size);
let checksum_end = 4;
- let checksum_as_bytes = &rest[..checksum_end]
+ let checksum_as_bytes = &bytes[..checksum_end]
.try_into()
.map_err(|_| PacketError::InvalidPacket)?;
let checksum = u32::from_be_bytes(*checksum_as_bytes);
if packet.checksum != checksum {
Err(PacketError::InvalidChecksum)
}
else {
Ok((packet, &bytes[checksum_end..]))
}
}
+
+ pub fn payload(&self) -> &[u8] {
+ self.payload
+ }
+
+ pub fn size(&self) -> u8 {
+ self.size
+ }
+
+ pub fn checksum(&self) -> u32 {
+ self.checksum
+ }
+
+ pub fn version(&self) -> u8 {
+ self.version
+ }
}
pub struct PacketSerializer<'a> {
bytes: &'a [u8],
packet_size: u8,
}
impl<'a> PacketSerializer<'a> {
pub fn from(bytes: &'a [u8], packet_size: u8) -> PacketSerializer<'a> {
PacketSerializer {
bytes,
packet_size,
}
}
}
impl<'a> Iterator for PacketSerializer<'a> {
type Item = Packet<'a>;
fn next(&mut self) -> Option<Self::Item> {
- if self.bytes.len() > 0 {
+ if !self.bytes.is_empty() {
let (packet, bytes) = Packet::from_source(&self.bytes, self.packet_size);
self.bytes = bytes;
Some(packet)
}
else {
None
}
}
}
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> {
self
.to_packets(packet_size)
.flat_map(|p| p.serialize())
.collect()
}
fn from_packet_data(mut packet_data: &[u8]) -> Result<Self, PacketError> {
let mut bytes: Vec<u8> = Vec::new();
- while packet_data.len() > 0 {
+ while !packet_data.is_empty() {
let (packet, rest) = Packet::deserialize(packet_data)?;
bytes.extend_from_slice(packet.payload());
packet_data = rest;
}
String::from_utf8(bytes)
.map_err(|_| PacketError::CorruptedMessage)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[should_panic]
fn from_source_panics_on_size_0() {
let _ = Packet::from_source(b"abc", 0);
}
#[test]
fn from_source_exact_size() {
let source = b"abcd";
let packet_size = source.len() as u8;
let (packet, rest) = Packet::from_source(source, packet_size);
assert!(rest.is_empty());
assert_eq!(source, packet.payload());
}
#[test]
fn from_source_greater_size() {
let source = b"abcd";
let packet_size = source.len() as u8;
let (packet, rest) = Packet::from_source(source, packet_size + 1);
assert!(rest.is_empty());
assert_eq!(source, packet.payload());
}
#[test]
fn from_source_smaller_size() {
let source = b"abcd";
let packet_size = source.len() as u8;
let (packet, rest) = Packet::from_source(source, packet_size - 1);
assert_eq!(packet.payload(), b"abc");
assert_eq!(rest, b"d");
}
#[test]
fn serialize() {
use std::convert::TryInto;
let source = b"abcd";
let header_size = 2;
let checksum_size = 4;
let (packet, _) = Packet::from_source(source, source.len() as u8);
let checksum_start = (header_size + packet.size()) as usize;
let bytes = packet.serialize();
let checksum_bytes = &bytes[checksum_start..].try_into().unwrap();
assert_eq!(bytes.len() as u8, packet.size() + header_size + checksum_size);
assert_eq!(bytes[0], packet.version());
assert_eq!(bytes[1], packet.size());
assert_eq!(&bytes[2..checksum_start], packet.payload());
assert_eq!(u32::from_be_bytes(*checksum_bytes), packet.checksum());
+ }
+
+ #[test]
+ fn deserialize_with_no_version_returns_invalid_packet_error() {
+ use PacketError::InvalidPacket;
+ assert_eq!(Packet::deserialize(&[]).unwrap_err(), InvalidPacket);
+ }
+ #[test]
+ fn deserialize_with_no_size_returns_invalid_packet_error() {
+ use PacketError::InvalidPacket;
+ assert_eq!(Packet::deserialize(&[0]).unwrap_err(), InvalidPacket);
+ }
+ #[test]
+ fn deserialize_with_size_0_returns_invalid_packet_error() {
+ use PacketError::InvalidPacket;
+ assert_eq!(Packet::deserialize(&[1, 0]).unwrap_err(), InvalidPacket);
+ }
+ #[test]
+ fn deserialize_with_invalid_version_returns_unknown_version_err_no_matter_the_size() {
+ use PacketError::UnknownProtocolVersion;
+ assert_eq!(Packet::deserialize(&[0, 1]).unwrap_err(), UnknownProtocolVersion);
+ assert_eq!(Packet::deserialize(&[0, 0]).unwrap_err(), UnknownProtocolVersion);
+ }
+ #[test]
+ fn deserialize_with_single_valid_packet() {
+ let source = b"hello";
+ let (packet, _) = Packet::from_source(source, source.len() as u8);
+ let data = packet.serialize();
+
+ let (packet, bytes) = Packet::deserialize(&data).unwrap();
+
+ assert!(bytes.is_empty());
+ assert_eq!(packet.payload(), source);
}
}
Илиян качи решение на 02.12.2019 15:14 (преди почти 6 години)
use std::fmt;
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum PacketError {
InvalidPacket,
InvalidChecksum,
UnknownProtocolVersion,
CorruptedMessage,
}
impl fmt::Display for PacketError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let error = match self {
PacketError::InvalidPacket => "Invalid packet",
PacketError::InvalidChecksum => "Invalid checksum",
PacketError::CorruptedMessage => "Corrupted message",
PacketError::UnknownProtocolVersion => "Unknown protocol version",
};
write!(f, "Packet error: {}", error)
}
}
impl std::error::Error for PacketError { }
#[derive(PartialEq, Debug)]
pub struct Packet<'a> {
version: u8,
size: u8,
payload: &'a [u8],
checksum: u32,
}
impl<'a> Packet<'a> {
pub fn from_source(source: &'a [u8], size: u8) -> (Packet<'a>, &'a [u8]) {
if size == 0 {
panic!("size 0 passed to Packet::from_source");
}
use std::cmp::min;
let size = min(source.len(), size as usize);
let payload = &source[..size];
let rest = &source[size..];
let checksum = payload.iter().fold(0, |acc, x| acc + *x as u32);
let packet = Packet {
version: 1,
size: size as u8,
payload,
checksum,
};
(packet, rest)
}
pub fn serialize(&self) -> Vec<u8> {
[self.version, self.size]
.iter()
.chain(self.payload)
.chain(&self.checksum.to_be_bytes())
.copied()
.collect()
}
Добра употреба на итератори във функционален стил.
pub fn deserialize(bytes: &[u8]) -> Result<(Packet, &[u8]), PacketError> {
let mut iter = bytes.iter();
let version = iter.next();
let size = iter.next();
match (version, size) {
(None, _) => Err(PacketError::InvalidPacket),
(_, None) => Err(PacketError::InvalidPacket),
(Some(1), Some(0)) => Err(PacketError::InvalidPacket),
(Some(1), Some(s)) => Packet::deserizalize_from_source(&bytes[2..], *s),
(Some(_), _) => Err(PacketError::UnknownProtocolVersion),
}
}
fn deserizalize_from_source(bytes: &[u8], size: u8) -> Result<(Packet, &[u8]), PacketError> {
use std::convert::TryInto;
let (packet, bytes) = Packet::from_source(bytes, size);
let checksum_end = 4;
let checksum_as_bytes = &bytes[..checksum_end]
.try_into()
.map_err(|_| PacketError::InvalidPacket)?;
Проблема е в това индексиране -- checksum_end
в невалиден пакет може да е отвъд границата на масива, така че това panic-ва.
Иначе, името на функцията има и малко typo -- "deserizalize" вместо "deserialize" :)
let checksum = u32::from_be_bytes(*checksum_as_bytes);
if packet.checksum != checksum {
Err(PacketError::InvalidChecksum)
}
else {
Ok((packet, &bytes[checksum_end..]))
}
}
pub fn payload(&self) -> &[u8] {
self.payload
}
pub fn size(&self) -> u8 {
self.size
}
pub fn checksum(&self) -> u32 {
self.checksum
}
pub fn version(&self) -> u8 {
self.version
}
}
pub struct PacketSerializer<'a> {
bytes: &'a [u8],
packet_size: u8,
}
impl<'a> PacketSerializer<'a> {
pub fn from(bytes: &'a [u8], packet_size: u8) -> PacketSerializer<'a> {
PacketSerializer {
bytes,
packet_size,
}
}
}
impl<'a> Iterator for PacketSerializer<'a> {
type Item = Packet<'a>;
fn next(&mut self) -> Option<Self::Item> {
if !self.bytes.is_empty() {
let (packet, bytes) = Packet::from_source(&self.bytes, self.packet_size);
self.bytes = bytes;
Some(packet)
}
else {
None
}
}
}
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> {
self
.to_packets(packet_size)
.flat_map(|p| p.serialize())
.collect()
}
fn from_packet_data(mut packet_data: &[u8]) -> Result<Self, PacketError> {
let mut bytes: Vec<u8> = Vec::new();
while !packet_data.is_empty() {
let (packet, rest) = Packet::deserialize(packet_data)?;
bytes.extend_from_slice(packet.payload());
packet_data = rest;
}
String::from_utf8(bytes)
.map_err(|_| PacketError::CorruptedMessage)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[should_panic]
fn from_source_panics_on_size_0() {
let _ = Packet::from_source(b"abc", 0);
}
#[test]
fn from_source_exact_size() {
let source = b"abcd";
let packet_size = source.len() as u8;
let (packet, rest) = Packet::from_source(source, packet_size);
assert!(rest.is_empty());
assert_eq!(source, packet.payload());
}
#[test]
fn from_source_greater_size() {
let source = b"abcd";
let packet_size = source.len() as u8;
let (packet, rest) = Packet::from_source(source, packet_size + 1);
assert!(rest.is_empty());
assert_eq!(source, packet.payload());
}
#[test]
fn from_source_smaller_size() {
let source = b"abcd";
let packet_size = source.len() as u8;
let (packet, rest) = Packet::from_source(source, packet_size - 1);
assert_eq!(packet.payload(), b"abc");
assert_eq!(rest, b"d");
}
#[test]
fn serialize() {
use std::convert::TryInto;
let source = b"abcd";
let header_size = 2;
let checksum_size = 4;
let (packet, _) = Packet::from_source(source, source.len() as u8);
let checksum_start = (header_size + packet.size()) as usize;
let bytes = packet.serialize();
let checksum_bytes = &bytes[checksum_start..].try_into().unwrap();
assert_eq!(bytes.len() as u8, packet.size() + header_size + checksum_size);
assert_eq!(bytes[0], packet.version());
assert_eq!(bytes[1], packet.size());
assert_eq!(&bytes[2..checksum_start], packet.payload());
assert_eq!(u32::from_be_bytes(*checksum_bytes), packet.checksum());
}
#[test]
fn deserialize_with_no_version_returns_invalid_packet_error() {
use PacketError::InvalidPacket;
assert_eq!(Packet::deserialize(&[]).unwrap_err(), InvalidPacket);
}
#[test]
fn deserialize_with_no_size_returns_invalid_packet_error() {
use PacketError::InvalidPacket;
assert_eq!(Packet::deserialize(&[0]).unwrap_err(), InvalidPacket);
}
#[test]
fn deserialize_with_size_0_returns_invalid_packet_error() {
use PacketError::InvalidPacket;
assert_eq!(Packet::deserialize(&[1, 0]).unwrap_err(), InvalidPacket);
}
#[test]
fn deserialize_with_invalid_version_returns_unknown_version_err_no_matter_the_size() {
use PacketError::UnknownProtocolVersion;
assert_eq!(Packet::deserialize(&[0, 1]).unwrap_err(), UnknownProtocolVersion);
assert_eq!(Packet::deserialize(&[0, 0]).unwrap_err(), UnknownProtocolVersion);
}
#[test]
fn deserialize_with_single_valid_packet() {
let source = b"hello";
let (packet, _) = Packet::from_source(source, source.len() as u8);
let data = packet.serialize();
let (packet, bytes) = Packet::deserialize(&data).unwrap();
assert!(bytes.is_empty());
assert_eq!(packet.payload(), source);
}
+
+ #[test]
+ fn string_to_packets_produces_a_valid_range() {
+ let source = String::from("msg msg");
+ let packet_size = b"msg ".len();
+ let mut iter = source.to_packets(packet_size as u8);
+ let packet_1 = iter.next();
+ let packet_2 = iter.next();
+ let packet_3 = iter.next();
+
+ assert!(packet_1.is_some());
+ assert!(packet_2.is_some());
+ assert!(packet_3.is_none());
+ assert_eq!(packet_1.unwrap().payload(), b"msg ");
+ assert_eq!(packet_2.unwrap().payload(), b"msg");
+ }
+
+ #[test]
+ fn string_from_packet_data_and_to_packet_data_compose_to_identity() {
+ let strings = [
+ "热瓦普 壳仔弦 馬骨胡 河北梆子".to_owned(),
+ "弹拨尔 河北梆子".to_owned(),
+ "京二胡河北梆子".to_owned(),
+ "изненада".to_owned(),
+ ];
+
+ for s in &strings {
+ let result = String::from_packet_data(&s.to_packet_data(3)).unwrap();
+ assert_eq!(*s, result);
+ }
+ }
}
Добро решение, дори и да си изпуснал един мъничък бъг. Харесват ми и тестовете, получаваш бонус точка за тях.
Добра употреба на итератори във функционален стил.
Проблема е в това индексиране --
checksum_end
в невалиден пакет може да е отвъд границата на масива, така че това panic-ва.Иначе, името на функцията има и малко typo -- "deserizalize" вместо "deserialize" :)