Решение на Network Packets от Стефан Чолаков

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

Към профила на Стефан Чолаков

Резултати

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

Код

use std::fmt;
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 => f.write_str("Invalid packet!"),
PacketError::InvalidChecksum => f.write_str("The checksum is invalid!"),
PacketError::UnknownProtocolVersion => f.write_str("The version of the protocol is unknown!"),
PacketError::CorruptedMessage => f.write_str("The message was corrupted!"),
}
}
}
impl std::error::Error for PacketError {}
#[derive(PartialEq, Debug)]
pub struct Packet<'a> {
version: u8,
message_size: u8,
message: &'a [u8],
checksum: u32
}
impl<'a> Packet<'a> {
pub fn from_source(source: &'a [u8], size: u8) -> (Self, &'a [u8]) {
if size == 0 {
panic!("Cannot create packet with size 0!");
}
let mut message_size = size;
if source.len() < message_size as usize {
message_size = source.len().try_into().unwrap();
}
let mut checksum :u32 = 0;
let (message,rest) = source.split_at(message_size as usize);
for &m in message {
checksum += m as u32;
}
(Packet {version: 1, message_size, message, checksum}, rest)
}
pub fn payload(&self) -> &[u8] {
self.message
}
pub fn serialize(&self) -> Vec<u8> {
let mut serialized : Vec<u8> = Vec::new();
serialized.push(self.version);
serialized.push(self.message_size);
for &b in self.payload() {
serialized.push(b);
}
for &c in &(self.checksum).to_be_bytes() {
serialized.push(c);
}
serialized
}
pub fn deserialize(bytes: &'a [u8]) -> Result<(Packet, &'a [u8]), PacketError> {
let bytes_len = bytes.len();
if bytes_len <= 6 { // version + size + 0 data + checksum
return Err(PacketError::InvalidPacket)
}
if bytes[0] != 1 {
return Err(PacketError::UnknownProtocolVersion)
}
let msg_size = bytes[1];
let expected_packet_len = 6 + msg_size;
if bytes_len < expected_packet_len as usize {
return Err(PacketError::InvalidPacket)
}else{
let k = 2 + msg_size as usize;
let payload = &bytes[2..k];
let checksum = as_u32_to_be(&bytes[k..k+4]);
let rest = &bytes[k+4..];
let mut expected_checksum :u32 = 0;
for &m in payload {
expected_checksum += m as u32;
}
if expected_checksum != checksum{
return Err(PacketError::InvalidChecksum)
}
let (packet,_) = Packet::from_source(payload,msg_size);
Ok((packet,rest))
}
}
}
pub fn as_u32_to_be(array: &[u8]) -> u32 {
((array[0] as u32) << 24) +
((array[1] as u32) << 16) +
((array[2] as u32) << 8) +
((array[3] as u32) << 0)
}
pub struct PacketSerializer<'a> {
packets:Vec<Packet<'a>>,
}
impl<'a> PacketSerializer<'a>{
pub fn new(packets:Vec<Packet<'a>>) -> Self {
PacketSerializer {packets}
}
}
impl<'b> Iterator for PacketSerializer<'b> {
type Item = Packet<'b>;
fn next(&mut self) -> Option<Self::Item> {
self.packets.pop()
}

Единия от бъговете ти идва тук -- метода pop премахва елемент от края на вектора, а метода push добавя елементи към края. Ти използваш тези два метода, което кара вектора ти да се държи като стек, и резултата излиза наобратно.

Тук би могъл да викнеш .remove(0) и да минат още 3 теста. Ако искаш да поддържаш ефективност, можеш да използваш std::collections::VecDeque вместо вектор.

}
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 {
let mut packets :Vec<Packet> = Vec::new();
let self_as_bytes = self.as_bytes();
let self_len = self.len();
if self_len == 0 {
return PacketSerializer::new(packets);
}
let mut real_packet_size = packet_size;
if self_len < packet_size as usize {
real_packet_size = self_len.try_into().unwrap();
}
let mut start = 0;
let mut end = real_packet_size;
while (end as usize) <= self_len {
let current_payload = self_as_bytes.get(start as usize .. end as usize).unwrap();
let (packet,_) = Packet::from_source(current_payload,real_packet_size);
packets.push(packet);
start = end;
if !((start as usize + 1) < self_len) {
break;
}
if ((start + real_packet_size) as usize) > self_len {
real_packet_size = (self_len - start as usize).try_into().unwrap();
}
end = start + real_packet_size;
}

Тук е другия ти бъг, но честно казано не мога да го намеря, понеже кода е твърде сложен :). Off-by-one грешка е, което е често срещана ситуация. Наистина, най-лесния начин да избягваш бъгове е да пишеш прост код, но може да е предизвикателно, admittedly.

Целта на Packet::from_source беше да се използва итеративно. Ако вместо да пазиш вектор от пакети, си пазеше slice-а от байтове, можеше да си улесниш доста живота като го викаш в цикъл върху това, което връща като "остатък". Ето моята имплементация:

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)
    }
}

Оттам нататък, .to_packets се свежда просто до:

fn to_packets(&self, packet_size: u8) -> PacketSerializer {
    PacketSerializer { source: self.as_bytes(), packet_size }
}

Дори да предпочиташ да си направиш вектор, който да подадеш на итератора, това пак щеше да може да се направи със същата логика преместена в .to_packets.

Разбира се, не искаме всички да пишат едни и същи решения, и не очакваме да "познаете" как сме решили ние проблема. Но в някои отношения е добре да се вгледаш в инструментите, които ти дава интерфейса и да се възползваш от тях. Ти конструираш пакет, но остатъка който се връща го игнорираш напълно. Опитваш се да изчислиш "истинския" packet size, но метода .from_source вече го прави.

PacketSerializer::new(packets)
}
fn to_packet_data(&self, packet_size: u8) -> Vec<u8> {
let mut packet_data:Vec<u8> = Vec::new();
let mut packet_serializer = self.to_packets(packet_size);
while let Some(entry) = packet_serializer.next() {
for m in entry.serialize(){
packet_data.push(m);
}
}
packet_data
}
fn from_packet_data(packet_data: &[u8]) -> Result<Self, PacketError> {
let mut vec:Vec<u8> = Vec::new();
let packet_data_len = packet_data.len();
if packet_data_len <= 6 {
return Err(PacketError::InvalidPacket)
}
let mut current_packet_len = packet_data[1] + 6;
let mut current_end = current_packet_len;
let mut current_start = 0;
while (current_end as usize) <= packet_data_len {
let (current_packet,_) = Packet::deserialize(&packet_data[current_start as usize ..current_end as usize])?;
let packet_payload = current_packet.payload();
for &b in packet_payload {
vec.push(b);
}
current_start = current_end;
if !((current_start as usize + 1) < packet_data_len) {
break;
}
current_packet_len = packet_data[current_start as usize + 1] + 6;
current_end = current_start + current_packet_len;
}
let result = String::from_utf8(vec);
if result.is_err() {
return Err(PacketError::CorruptedMessage)
}
Ok(result.unwrap())
}
}

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

Compiling solution v0.1.0 (/tmp/d20200111-2173579-iutls9/solution)
    Finished test [unoptimized + debuginfo] target(s) in 3.68s
     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 ... thread '<unnamed>' panicked at 'assertion failed: `(left == right)`
  left: `"bazbar foo "`,
 right: `"foo bar baz"`', tests/solution_test.rs:204:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
FAILED
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 ... thread '<unnamed>' panicked at 'assertion failed: `(left == right)`
  left: `"foo bar baz"`,
 right: `"foo bar ba"`', tests/solution_test.rs:215:9
FAILED
test solution_test::test_full_roundtrip_for_zero_size_string ... thread '<unnamed>' panicked at 'called `Result::unwrap()` on an `Err` value: InvalidPacket', src/libcore/result.rs:1165:5
FAILED
test solution_test::test_invalid_packet_combination ... ok
test solution_test::test_iterating_packets ... thread '<unnamed>' panicked at 'assertion failed: `(left == right)`
  left: `[98, 97, 122]`,
 right: `[102, 111, 111, 32]`', tests/solution_test.rs:178:9
FAILED
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_consuming_packets stdout ----
thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `"bazbar foo "`,
 right: `"foo bar baz"`', tests/solution_test.rs:197:5

---- solution_test::test_full_roundtrip stdout ----
thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `"foo bar baz"`,
 right: `"foo bar ba"`', tests/solution_test.rs:210:5

---- solution_test::test_full_roundtrip_for_zero_size_string stdout ----
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: InvalidPacket', tests/solution_test.rs:221:5

---- solution_test::test_iterating_packets stdout ----
thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `[98, 97, 122]`,
 right: `[102, 111, 111, 32]`', tests/solution_test.rs:172:5


failures:
    solution_test::test_consuming_packets
    solution_test::test_full_roundtrip
    solution_test::test_full_roundtrip_for_zero_size_string
    solution_test::test_iterating_packets

test result: FAILED. 11 passed; 4 failed; 0 ignored; 0 measured; 0 filtered out

error: test failed, to rerun pass '--test solution_test'

История (6 версии и 4 коментара)

Стефан качи първо решение на 28.11.2019 17:53 (преди почти 6 години)

Стефан качи решение на 28.11.2019 22:46 (преди почти 6 години)

use std::fmt;
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 => f.write_str("Invalid packet!"),
PacketError::InvalidChecksum => f.write_str("The checksum is invalid!"),
PacketError::UnknownProtocolVersion => f.write_str("The version of the protocol is unknown!"),
PacketError::CorruptedMessage => f.write_str("The message was corrupted!"),
}
}
}
impl std::error::Error for PacketError {}
#[derive(PartialEq, Debug)]
pub struct Packet<'a> {
version: u8,
message_size: u8,
message: &'a [u8],
checksum: u32
}
impl<'a> Packet<'a> {
/// Конструира пакет от дадения slice от байтове. Приема параметър `size`, който е размера на
/// payload-а на новия пакет. Връща двойка от пакет + оставащите байтове. Тоест, ако имате низа
/// "abcd" и викнете метода върху байтовата му репрезентация с параметър `size` равен на 3, ще
/// върнете двойката `(<пакет с payload "abc">, <байтовия низ "d">)`.
///
/// Байтове от низ можете да извадите чрез `.as_bytes()`, можете и да си конструирате байтов
/// литерал като b"abcd".
///
/// Ако подадения `size` е по-голям от дължината на `source`, приемаме, че размера ще е точно
/// дължината на `source` (и остатъка ще е празен slice).
///
/// Ако параметъра `size` е 0, очакваме тази функция да panic-не (приемаме, че това извикване
/// просто е невалидно, програмистка грешка).
///
pub fn from_source(source: &'a [u8], size: u8) -> (Self, &'a [u8]) {
if size == 0 {
panic!("Cannot create packet with size 0!");
}
let mut message_size = size;
if source.len() < message_size as usize {
message_size = source.len().try_into().unwrap();
}
let mut checksum :u32 = 0;
let (message,rest) = source.split_at(message_size as usize);
for &m in message {
checksum += m as u32;
}
checksum = checksum.to_be();
(Packet {version: 1, message_size, message, checksum}, rest)
}
- /// Връща само slice-а който пакета опакова. Тоест, ако сме конструирали пакета със
/// `Packet::from_source(b"abc", 3)`, очакваме `.payload()` да ни върне `b"abc"`.
pub fn payload(&self) -> &[u8] { // ligid??
self.message
}
/// Сериализира пакета, тоест превръща го в байтове, готови за трансфер. Версия, дължина,
/// съобщение (payload), checksum. Вижте по-горе за детайлно обяснение.
///
pub fn serialize(&self) -> Vec<u8> {
let mut serialized : Vec<u8> = Vec::new();
serialized.push(self.version);
serialized.push(self.message_size);
for &b in self.payload() {
serialized.push(b);
}
for &c in &(self.checksum).to_be_bytes() {
serialized.push(c);
}
serialized
}
// Note: if size is less, it'll be the checksum not being valid
/// Имайки slice от байтове, искаме да извадим един пакет от началото и да върнем остатъка,
/// пакетиран в `Result`.
///
/// Ако липсват версия, размер, чексума, или размера е твърде малък, за да може да се изпарси //done
/// валиден пакет от байтовете, връщаме грешка `PacketError::InvalidPacket`.
///
/// Ако версията е различна от 1, връщаме `PacketError::UnknownProtocolVersion`. //done
///
/// Ако checksum-а, който прочитаме от последните 4 байта на пакета е различен от изчисления
/// checksum на payload-а (сумата от байтовете му), връщаме `PacketError::InvalidChecksum`.
///
/// Забележете, че ако размера е по-голям от истинския размер на payload-а, се очаква
/// `PacketError::InvalidPacket`. Ако размера е по-малък от истинския размер на payload-а,
/// въпросния ще се изпарси, но чексумата ще е грешна, така че ще очакваме
/// `PacketError::InvalidChecksum`.
- pub fn deserialize(bytes: &'a [u8]) -> Result<(Packet, &'a [u8]), PacketError> { // every tyme empty rest slice??
+ pub fn deserialize(bytes: &'a [u8]) -> Result<(Packet, &'a [u8]), PacketError> {
let bytes_len = bytes.len();
if bytes_len <= 6 { // version + size + 0 data + checksum
return Err(PacketError::InvalidPacket)
}
if bytes[0] != 1 {
return Err(PacketError::UnknownProtocolVersion)
}
let msg_size: u8 = bytes[1];
let expected_packet_len = 6 + msg_size;
if bytes_len < expected_packet_len as usize {
return Err(PacketError::InvalidPacket)
- } else if bytes_len > expected_packet_len as usize {
- return Err(PacketError::InvalidChecksum)////!!!!!!!!
}else{
- let payload = &bytes[2..msg_size as usize + 2];
let k = 2 + msg_size as usize;
- let checksum = as_u32_be(&bytes[k..]);
+ let payload = &bytes[2..k];
+ let mut checksum = as_u32(&bytes[k..k+4]);
+ let rest = &bytes[k+4..];
+
let mut expected_checksum :u32 = 0;
for &m in payload {
expected_checksum += m as u32;
}
- expected_checksum = expected_checksum.to_be();
+ checksum = checksum.to_be();
if expected_checksum != checksum{
return Err(PacketError::InvalidChecksum)
}
- Ok(Packet::from_source(bytes,msg_size))
+ let (packet,_) = Packet::from_source(payload,msg_size);
+ Ok((packet,rest))
}
}
}
-pub fn as_u32_be(array: &[u8]) -> u32 {
+pub fn as_u32(array: &[u8]) -> u32 {
((array[0] as u32) << 24) +
((array[1] as u32) << 16) +
((array[2] as u32) << 8) +
((array[3] as u32) << 0)
}
/// Структура, която ще служи за итериране по пакети. Ще я конструираме от някакво съобщение, и
/// итерацията ще връща всеки следващ пакет, докато съобщението не бъде напълно "изпратено".
/// Изберете каквито полета ви трябват.
///
/// Може да е нужно да добавите lifetimes на дефиницията тук и/или на методите в impl блока.
///
pub struct PacketSerializer<'a> {
packets:Vec<Packet<'a>>,
}
impl<'a> PacketSerializer<'a>{
pub fn new(packets:Vec<Packet<'a>>) -> Self {
PacketSerializer {packets}
}
}
impl<'b> Iterator for PacketSerializer<'b> {
type Item = Packet<'b>;
fn next(&mut self) -> Option<Self::Item> {
self.packets.pop()
}
}
/// Този 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 { // all should be same size????
+ fn to_packets(&self, packet_size: u8) -> PacketSerializer {
let mut packets :Vec<Packet> = Vec::new();
let self_as_bytes = self.as_bytes();
let self_len = self.len();
let mut real_packet_size = packet_size;
if self_len < packet_size as usize {
real_packet_size = self_len.try_into().unwrap();
}
let mut start = 0;
let mut end = real_packet_size;
- while (end as usize) <= self_len { // = needed here if we have only one string
+ while (end as usize) <= self_len {
let current_payload = self_as_bytes.get(start as usize ..end as usize).unwrap();
let (packet,_) = Packet::from_source(current_payload,real_packet_size);
packets.push(packet);
start = end;
- if !((start as usize + 1) < self_len) {//there is not next payload
+ if !((start as usize + 1) < self_len) {
break;
}
+ if ((start + real_packet_size) as usize) > self_len {
+ real_packet_size = (self_len - start as usize).try_into().unwrap();
+ }
end = start + real_packet_size;
}
PacketSerializer::new(packets)
}
/// Имайки итератор по пакети, лесно можем да сериализираме всеки индивидуален пакет в поредица
/// от байтове със `.serialize()` и да го натъпчем във вектора.
///
fn to_packet_data(&self, packet_size: u8) -> Vec<u8> {
let mut packet_data:Vec<u8> = Vec::new();
let mut packet_serializer = self.to_packets(packet_size);
while let Some(entry) = packet_serializer.next() {
for m in entry.serialize(){
packet_data.push(m);
}
}
packet_data
}
/// Обратното на горния метод е тази асоциирана функция -- имайки slice от байтове които са
/// сериализирана репрезентация на пакети, искаме да десериализираме пакети от този slice, да
/// им извадим payload-ите, и да ги сглобим в оригиналното съобщение.
///
/// Грешките, които могат да се върнат, са същите, които идват от `.deserialize()`.
///
/// Една допълнителна грешка, която може да се случи е при сглобяване на съобщението -- ако е
/// имало липсващ пакет, може съчетанието на байтовете да не генерира правилно UTF8 съобщение.
/// Тогава връщаме `PacketError::CorruptedMessage`.
///
- fn from_packet_data(packet_data: &[u8]) -> Result<Self, PacketError> { // empty slice here???
+ fn from_packet_data(packet_data: &[u8]) -> Result<Self, PacketError> {
let mut vec:Vec<u8> = Vec::new();
let packet_data_len = packet_data.len();
let mut current_packet_len = packet_data[1] + 6;
let mut current_end = current_packet_len;
let mut current_start = 0;
while (current_end as usize) < packet_data_len {
let (current_packet,_) = Packet::deserialize(&packet_data[current_start as usize ..current_end as usize])?;
let packet_payload = current_packet.payload();
for &b in packet_payload {
vec.push(b);
}
- // update start and end
current_start = current_end;
if !((current_start as usize + 2) < packet_data_len) {
break;
}
current_packet_len = packet_data[current_start as usize + 1] + 6;
current_end = current_start + current_packet_len;
}
let result = String::from_utf8(vec);
if result.is_err() {
return Err(PacketError::CorruptedMessage)
}
Ok(result.unwrap())
}
}

Стефан качи решение на 29.11.2019 11:54 (преди почти 6 години)

use std::fmt;
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 => f.write_str("Invalid packet!"),
PacketError::InvalidChecksum => f.write_str("The checksum is invalid!"),
PacketError::UnknownProtocolVersion => f.write_str("The version of the protocol is unknown!"),
PacketError::CorruptedMessage => f.write_str("The message was corrupted!"),
}
}
}
impl std::error::Error for PacketError {}
#[derive(PartialEq, Debug)]
pub struct Packet<'a> {
version: u8,
message_size: u8,
message: &'a [u8],
checksum: u32
}
impl<'a> Packet<'a> {
/// Конструира пакет от дадения slice от байтове. Приема параметър `size`, който е размера на
/// payload-а на новия пакет. Връща двойка от пакет + оставащите байтове. Тоест, ако имате низа
/// "abcd" и викнете метода върху байтовата му репрезентация с параметър `size` равен на 3, ще
/// върнете двойката `(<пакет с payload "abc">, <байтовия низ "d">)`.
///
/// Байтове от низ можете да извадите чрез `.as_bytes()`, можете и да си конструирате байтов
/// литерал като b"abcd".
///
/// Ако подадения `size` е по-голям от дължината на `source`, приемаме, че размера ще е точно
/// дължината на `source` (и остатъка ще е празен slice).
///
/// Ако параметъра `size` е 0, очакваме тази функция да panic-не (приемаме, че това извикване
/// просто е невалидно, програмистка грешка).
///
pub fn from_source(source: &'a [u8], size: u8) -> (Self, &'a [u8]) {
if size == 0 {
panic!("Cannot create packet with size 0!");
}
let mut message_size = size;
if source.len() < message_size as usize {
message_size = source.len().try_into().unwrap();
}
let mut checksum :u32 = 0;
let (message,rest) = source.split_at(message_size as usize);
for &m in message {
checksum += m as u32;
}
- checksum = checksum.to_be();
(Packet {version: 1, message_size, message, checksum}, rest)
}
/// `Packet::from_source(b"abc", 3)`, очакваме `.payload()` да ни върне `b"abc"`.
pub fn payload(&self) -> &[u8] { // ligid??
self.message
}
/// Сериализира пакета, тоест превръща го в байтове, готови за трансфер. Версия, дължина,
/// съобщение (payload), checksum. Вижте по-горе за детайлно обяснение.
///
pub fn serialize(&self) -> Vec<u8> {
let mut serialized : Vec<u8> = Vec::new();
serialized.push(self.version);
serialized.push(self.message_size);
for &b in self.payload() {
serialized.push(b);
}
for &c in &(self.checksum).to_be_bytes() {
serialized.push(c);
}
serialized
}
// Note: if size is less, it'll be the checksum not being valid
/// Имайки slice от байтове, искаме да извадим един пакет от началото и да върнем остатъка,
/// пакетиран в `Result`.
///
/// Ако липсват версия, размер, чексума, или размера е твърде малък, за да може да се изпарси //done
/// валиден пакет от байтовете, връщаме грешка `PacketError::InvalidPacket`.
///
/// Ако версията е различна от 1, връщаме `PacketError::UnknownProtocolVersion`. //done
///
/// Ако checksum-а, който прочитаме от последните 4 байта на пакета е различен от изчисления
/// checksum на payload-а (сумата от байтовете му), връщаме `PacketError::InvalidChecksum`.
///
/// Забележете, че ако размера е по-голям от истинския размер на payload-а, се очаква
/// `PacketError::InvalidPacket`. Ако размера е по-малък от истинския размер на payload-а,
/// въпросния ще се изпарси, но чексумата ще е грешна, така че ще очакваме
/// `PacketError::InvalidChecksum`.
pub fn deserialize(bytes: &'a [u8]) -> Result<(Packet, &'a [u8]), PacketError> {
let bytes_len = bytes.len();
if bytes_len <= 6 { // version + size + 0 data + checksum
return Err(PacketError::InvalidPacket)
}
if bytes[0] != 1 {
return Err(PacketError::UnknownProtocolVersion)
}
let msg_size: u8 = bytes[1];
let expected_packet_len = 6 + msg_size;
if bytes_len < expected_packet_len as usize {
return Err(PacketError::InvalidPacket)
}else{
let k = 2 + msg_size as usize;
let payload = &bytes[2..k];
- let mut checksum = as_u32(&bytes[k..k+4]);
+ let checksum = as_u32_to_be(&bytes[k..k+4]);
let rest = &bytes[k+4..];
let mut expected_checksum :u32 = 0;
for &m in payload {
expected_checksum += m as u32;
}
- checksum = checksum.to_be();
if expected_checksum != checksum{
return Err(PacketError::InvalidChecksum)
}
let (packet,_) = Packet::from_source(payload,msg_size);
Ok((packet,rest))
}
}
}
-pub fn as_u32(array: &[u8]) -> u32 {
+pub fn as_u32_to_be(array: &[u8]) -> u32 {
((array[0] as u32) << 24) +
((array[1] as u32) << 16) +
((array[2] as u32) << 8) +
((array[3] as u32) << 0)
}
/// Структура, която ще служи за итериране по пакети. Ще я конструираме от някакво съобщение, и
/// итерацията ще връща всеки следващ пакет, докато съобщението не бъде напълно "изпратено".
/// Изберете каквито полета ви трябват.
///
/// Може да е нужно да добавите lifetimes на дефиницията тук и/или на методите в impl блока.
///
pub struct PacketSerializer<'a> {
packets:Vec<Packet<'a>>,
}
impl<'a> PacketSerializer<'a>{
pub fn new(packets:Vec<Packet<'a>>) -> Self {
PacketSerializer {packets}
}
}
impl<'b> Iterator for PacketSerializer<'b> {
type Item = Packet<'b>;
fn next(&mut self) -> Option<Self::Item> {
self.packets.pop()
}
}
/// Този 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 {
let mut packets :Vec<Packet> = Vec::new();
let self_as_bytes = self.as_bytes();
let self_len = self.len();
let mut real_packet_size = packet_size;
if self_len < packet_size as usize {
real_packet_size = self_len.try_into().unwrap();
}
let mut start = 0;
let mut end = real_packet_size;
while (end as usize) <= self_len {
let current_payload = self_as_bytes.get(start as usize ..end as usize).unwrap();
let (packet,_) = Packet::from_source(current_payload,real_packet_size);
packets.push(packet);
start = end;
if !((start as usize + 1) < self_len) {
break;
}
if ((start + real_packet_size) as usize) > self_len {
real_packet_size = (self_len - start as usize).try_into().unwrap();
}
end = start + real_packet_size;
}
PacketSerializer::new(packets)
}
/// Имайки итератор по пакети, лесно можем да сериализираме всеки индивидуален пакет в поредица
/// от байтове със `.serialize()` и да го натъпчем във вектора.
///
fn to_packet_data(&self, packet_size: u8) -> Vec<u8> {
let mut packet_data:Vec<u8> = Vec::new();
let mut packet_serializer = self.to_packets(packet_size);
while let Some(entry) = packet_serializer.next() {
for m in entry.serialize(){
packet_data.push(m);
}
}
packet_data
}
/// Обратното на горния метод е тази асоциирана функция -- имайки slice от байтове които са
/// сериализирана репрезентация на пакети, искаме да десериализираме пакети от този slice, да
/// им извадим payload-ите, и да ги сглобим в оригиналното съобщение.
///
/// Грешките, които могат да се върнат, са същите, които идват от `.deserialize()`.
///
/// Една допълнителна грешка, която може да се случи е при сглобяване на съобщението -- ако е
/// имало липсващ пакет, може съчетанието на байтовете да не генерира правилно UTF8 съобщение.
/// Тогава връщаме `PacketError::CorruptedMessage`.
///
fn from_packet_data(packet_data: &[u8]) -> Result<Self, PacketError> {
let mut vec:Vec<u8> = Vec::new();
let packet_data_len = packet_data.len();
let mut current_packet_len = packet_data[1] + 6;
let mut current_end = current_packet_len;
let mut current_start = 0;
while (current_end as usize) < packet_data_len {
let (current_packet,_) = Packet::deserialize(&packet_data[current_start as usize ..current_end as usize])?;
let packet_payload = current_packet.payload();
for &b in packet_payload {
vec.push(b);
}
current_start = current_end;
if !((current_start as usize + 2) < packet_data_len) {
break;
}
current_packet_len = packet_data[current_start as usize + 1] + 6;
current_end = current_start + current_packet_len;
}
let result = String::from_utf8(vec);
if result.is_err() {
return Err(PacketError::CorruptedMessage)
}
Ok(result.unwrap())
}
}

Стефан качи решение на 29.11.2019 16:51 (преди почти 6 години)

use std::fmt;
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 => f.write_str("Invalid packet!"),
PacketError::InvalidChecksum => f.write_str("The checksum is invalid!"),
PacketError::UnknownProtocolVersion => f.write_str("The version of the protocol is unknown!"),
PacketError::CorruptedMessage => f.write_str("The message was corrupted!"),
}
}
}
impl std::error::Error for PacketError {}
#[derive(PartialEq, Debug)]
pub struct Packet<'a> {
version: u8,
message_size: u8,
message: &'a [u8],
checksum: u32
}
impl<'a> Packet<'a> {
/// Конструира пакет от дадения slice от байтове. Приема параметър `size`, който е размера на
/// payload-а на новия пакет. Връща двойка от пакет + оставащите байтове. Тоест, ако имате низа
/// "abcd" и викнете метода върху байтовата му репрезентация с параметър `size` равен на 3, ще
/// върнете двойката `(<пакет с payload "abc">, <байтовия низ "d">)`.
///
/// Байтове от низ можете да извадите чрез `.as_bytes()`, можете и да си конструирате байтов
/// литерал като b"abcd".
///
/// Ако подадения `size` е по-голям от дължината на `source`, приемаме, че размера ще е точно
/// дължината на `source` (и остатъка ще е празен slice).
///
/// Ако параметъра `size` е 0, очакваме тази функция да panic-не (приемаме, че това извикване
/// просто е невалидно, програмистка грешка).
///
pub fn from_source(source: &'a [u8], size: u8) -> (Self, &'a [u8]) {
if size == 0 {
panic!("Cannot create packet with size 0!");
}
let mut message_size = size;
if source.len() < message_size as usize {
message_size = source.len().try_into().unwrap();
}
let mut checksum :u32 = 0;
let (message,rest) = source.split_at(message_size as usize);
for &m in message {
checksum += m as u32;
}
(Packet {version: 1, message_size, message, checksum}, rest)
}
- /// `Packet::from_source(b"abc", 3)`, очакваме `.payload()` да ни върне `b"abc"`.
- pub fn payload(&self) -> &[u8] { // ligid??
+ pub fn payload(&self) -> &[u8] {
self.message
}
- /// Сериализира пакета, тоест превръща го в байтове, готови за трансфер. Версия, дължина,
- /// съобщение (payload), checksum. Вижте по-горе за детайлно обяснение.
- ///
pub fn serialize(&self) -> Vec<u8> {
let mut serialized : Vec<u8> = Vec::new();
serialized.push(self.version);
serialized.push(self.message_size);
for &b in self.payload() {
serialized.push(b);
}
for &c in &(self.checksum).to_be_bytes() {
serialized.push(c);
}
serialized
}
- // Note: if size is less, it'll be the checksum not being valid
- /// Имайки slice от байтове, искаме да извадим един пакет от началото и да върнем остатъка,
- /// пакетиран в `Result`.
- ///
- /// Ако липсват версия, размер, чексума, или размера е твърде малък, за да може да се изпарси //done
- /// валиден пакет от байтовете, връщаме грешка `PacketError::InvalidPacket`.
- ///
- /// Ако версията е различна от 1, връщаме `PacketError::UnknownProtocolVersion`. //done
- ///
- /// Ако checksum-а, който прочитаме от последните 4 байта на пакета е различен от изчисления
- /// checksum на payload-а (сумата от байтовете му), връщаме `PacketError::InvalidChecksum`.
- ///
- /// Забележете, че ако размера е по-голям от истинския размер на payload-а, се очаква
- /// `PacketError::InvalidPacket`. Ако размера е по-малък от истинския размер на payload-а,
- /// въпросния ще се изпарси, но чексумата ще е грешна, така че ще очакваме
- /// `PacketError::InvalidChecksum`.
pub fn deserialize(bytes: &'a [u8]) -> Result<(Packet, &'a [u8]), PacketError> {
let bytes_len = bytes.len();
if bytes_len <= 6 { // version + size + 0 data + checksum
return Err(PacketError::InvalidPacket)
}
if bytes[0] != 1 {
return Err(PacketError::UnknownProtocolVersion)
}
- let msg_size: u8 = bytes[1];
+ let msg_size = bytes[1];
let expected_packet_len = 6 + msg_size;
if bytes_len < expected_packet_len as usize {
return Err(PacketError::InvalidPacket)
}else{
let k = 2 + msg_size as usize;
let payload = &bytes[2..k];
let checksum = as_u32_to_be(&bytes[k..k+4]);
let rest = &bytes[k+4..];
let mut expected_checksum :u32 = 0;
for &m in payload {
expected_checksum += m as u32;
}
if expected_checksum != checksum{
return Err(PacketError::InvalidChecksum)
}
let (packet,_) = Packet::from_source(payload,msg_size);
Ok((packet,rest))
}
}
}
pub fn as_u32_to_be(array: &[u8]) -> u32 {
((array[0] as u32) << 24) +
((array[1] as u32) << 16) +
((array[2] as u32) << 8) +
((array[3] as u32) << 0)
}
-/// Структура, която ще служи за итериране по пакети. Ще я конструираме от някакво съобщение, и
-/// итерацията ще връща всеки следващ пакет, докато съобщението не бъде напълно "изпратено".
-/// Изберете каквито полета ви трябват.
-///
-/// Може да е нужно да добавите lifetimes на дефиницията тук и/или на методите в impl блока.
-///
pub struct PacketSerializer<'a> {
packets:Vec<Packet<'a>>,
}
impl<'a> PacketSerializer<'a>{
pub fn new(packets:Vec<Packet<'a>>) -> Self {
PacketSerializer {packets}
}
}
impl<'b> Iterator for PacketSerializer<'b> {
type Item = Packet<'b>;
fn next(&mut self) -> Option<Self::Item> {
self.packets.pop()
}
}
-/// Този 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 {
let mut packets :Vec<Packet> = Vec::new();
let self_as_bytes = self.as_bytes();
let self_len = self.len();
let mut real_packet_size = packet_size;
if self_len < packet_size as usize {
real_packet_size = self_len.try_into().unwrap();
}
let mut start = 0;
let mut end = real_packet_size;
while (end as usize) <= self_len {
- let current_payload = self_as_bytes.get(start as usize ..end as usize).unwrap();
+ let current_payload = self_as_bytes.get(start as usize .. end as usize).unwrap();
let (packet,_) = Packet::from_source(current_payload,real_packet_size);
packets.push(packet);
start = end;
if !((start as usize + 1) < self_len) {
break;
}
if ((start + real_packet_size) as usize) > self_len {
real_packet_size = (self_len - start as usize).try_into().unwrap();
}
end = start + real_packet_size;
}
PacketSerializer::new(packets)
}
- /// Имайки итератор по пакети, лесно можем да сериализираме всеки индивидуален пакет в поредица
- /// от байтове със `.serialize()` и да го натъпчем във вектора.
- ///
fn to_packet_data(&self, packet_size: u8) -> Vec<u8> {
let mut packet_data:Vec<u8> = Vec::new();
let mut packet_serializer = self.to_packets(packet_size);
while let Some(entry) = packet_serializer.next() {
for m in entry.serialize(){
packet_data.push(m);
}
}
packet_data
}
- /// Обратното на горния метод е тази асоциирана функция -- имайки slice от байтове които са
- /// сериализирана репрезентация на пакети, искаме да десериализираме пакети от този slice, да
- /// им извадим payload-ите, и да ги сглобим в оригиналното съобщение.
- ///
- /// Грешките, които могат да се върнат, са същите, които идват от `.deserialize()`.
- ///
- /// Една допълнителна грешка, която може да се случи е при сглобяване на съобщението -- ако е
- /// имало липсващ пакет, може съчетанието на байтовете да не генерира правилно UTF8 съобщение.
- /// Тогава връщаме `PacketError::CorruptedMessage`.
- ///
fn from_packet_data(packet_data: &[u8]) -> Result<Self, PacketError> {
let mut vec:Vec<u8> = Vec::new();
let packet_data_len = packet_data.len();
+ if packet_data_len <= 6 {
+ return Err(PacketError::InvalidPacket)
+ }
let mut current_packet_len = packet_data[1] + 6;
let mut current_end = current_packet_len;
let mut current_start = 0;
- while (current_end as usize) < packet_data_len {
+ while (current_end as usize) <= packet_data_len {
let (current_packet,_) = Packet::deserialize(&packet_data[current_start as usize ..current_end as usize])?;
let packet_payload = current_packet.payload();
for &b in packet_payload {
vec.push(b);
}
current_start = current_end;
- if !((current_start as usize + 2) < packet_data_len) {
+ if !((current_start as usize + 1) < packet_data_len) {
break;
}
current_packet_len = packet_data[current_start as usize + 1] + 6;
current_end = current_start + current_packet_len;
}
let result = String::from_utf8(vec);
if result.is_err() {
return Err(PacketError::CorruptedMessage)
}
Ok(result.unwrap())
}
-
}

Стефан качи решение на 29.11.2019 16:52 (преди почти 6 години)

use std::fmt;
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 => f.write_str("Invalid packet!"),
PacketError::InvalidChecksum => f.write_str("The checksum is invalid!"),
PacketError::UnknownProtocolVersion => f.write_str("The version of the protocol is unknown!"),
PacketError::CorruptedMessage => f.write_str("The message was corrupted!"),
}
}
}
impl std::error::Error for PacketError {}
#[derive(PartialEq, Debug)]
pub struct Packet<'a> {
version: u8,
message_size: u8,
message: &'a [u8],
checksum: u32
}
impl<'a> Packet<'a> {
- /// Конструира пакет от дадения slice от байтове. Приема параметър `size`, който е размера на
- /// payload-а на новия пакет. Връща двойка от пакет + оставащите байтове. Тоест, ако имате низа
- /// "abcd" и викнете метода върху байтовата му репрезентация с параметър `size` равен на 3, ще
- /// върнете двойката `(<пакет с payload "abc">, <байтовия низ "d">)`.
- ///
- /// Байтове от низ можете да извадите чрез `.as_bytes()`, можете и да си конструирате байтов
- /// литерал като b"abcd".
- ///
- /// Ако подадения `size` е по-голям от дължината на `source`, приемаме, че размера ще е точно
- /// дължината на `source` (и остатъка ще е празен slice).
- ///
- /// Ако параметъра `size` е 0, очакваме тази функция да panic-не (приемаме, че това извикване
- /// просто е невалидно, програмистка грешка).
- ///
+
pub fn from_source(source: &'a [u8], size: u8) -> (Self, &'a [u8]) {
if size == 0 {
panic!("Cannot create packet with size 0!");
}
let mut message_size = size;
if source.len() < message_size as usize {
message_size = source.len().try_into().unwrap();
}
let mut checksum :u32 = 0;
let (message,rest) = source.split_at(message_size as usize);
for &m in message {
checksum += m as u32;
}
(Packet {version: 1, message_size, message, checksum}, rest)
}
pub fn payload(&self) -> &[u8] {
self.message
}
pub fn serialize(&self) -> Vec<u8> {
let mut serialized : Vec<u8> = Vec::new();
serialized.push(self.version);
serialized.push(self.message_size);
for &b in self.payload() {
serialized.push(b);
}
for &c in &(self.checksum).to_be_bytes() {
serialized.push(c);
}
serialized
}
pub fn deserialize(bytes: &'a [u8]) -> Result<(Packet, &'a [u8]), PacketError> {
let bytes_len = bytes.len();
if bytes_len <= 6 { // version + size + 0 data + checksum
return Err(PacketError::InvalidPacket)
}
if bytes[0] != 1 {
return Err(PacketError::UnknownProtocolVersion)
}
let msg_size = bytes[1];
let expected_packet_len = 6 + msg_size;
if bytes_len < expected_packet_len as usize {
return Err(PacketError::InvalidPacket)
}else{
let k = 2 + msg_size as usize;
let payload = &bytes[2..k];
let checksum = as_u32_to_be(&bytes[k..k+4]);
let rest = &bytes[k+4..];
let mut expected_checksum :u32 = 0;
for &m in payload {
expected_checksum += m as u32;
}
if expected_checksum != checksum{
return Err(PacketError::InvalidChecksum)
}
let (packet,_) = Packet::from_source(payload,msg_size);
Ok((packet,rest))
}
}
}
pub fn as_u32_to_be(array: &[u8]) -> u32 {
((array[0] as u32) << 24) +
((array[1] as u32) << 16) +
((array[2] as u32) << 8) +
((array[3] as u32) << 0)
}
pub struct PacketSerializer<'a> {
packets:Vec<Packet<'a>>,
}
impl<'a> PacketSerializer<'a>{
pub fn new(packets:Vec<Packet<'a>>) -> Self {
PacketSerializer {packets}
}
}
impl<'b> Iterator for PacketSerializer<'b> {
type Item = Packet<'b>;
fn next(&mut self) -> Option<Self::Item> {
self.packets.pop()
}
}
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 {
let mut packets :Vec<Packet> = Vec::new();
let self_as_bytes = self.as_bytes();
let self_len = self.len();
let mut real_packet_size = packet_size;
if self_len < packet_size as usize {
real_packet_size = self_len.try_into().unwrap();
}
let mut start = 0;
let mut end = real_packet_size;
while (end as usize) <= self_len {
let current_payload = self_as_bytes.get(start as usize .. end as usize).unwrap();
let (packet,_) = Packet::from_source(current_payload,real_packet_size);
packets.push(packet);
start = end;
if !((start as usize + 1) < self_len) {
break;
}
if ((start + real_packet_size) as usize) > self_len {
real_packet_size = (self_len - start as usize).try_into().unwrap();
}
end = start + real_packet_size;
}
PacketSerializer::new(packets)
}
fn to_packet_data(&self, packet_size: u8) -> Vec<u8> {
let mut packet_data:Vec<u8> = Vec::new();
let mut packet_serializer = self.to_packets(packet_size);
while let Some(entry) = packet_serializer.next() {
for m in entry.serialize(){
packet_data.push(m);
}
}
packet_data
}
fn from_packet_data(packet_data: &[u8]) -> Result<Self, PacketError> {
let mut vec:Vec<u8> = Vec::new();
let packet_data_len = packet_data.len();
if packet_data_len <= 6 {
return Err(PacketError::InvalidPacket)
}
let mut current_packet_len = packet_data[1] + 6;
let mut current_end = current_packet_len;
let mut current_start = 0;
while (current_end as usize) <= packet_data_len {
let (current_packet,_) = Packet::deserialize(&packet_data[current_start as usize ..current_end as usize])?;
let packet_payload = current_packet.payload();
for &b in packet_payload {
vec.push(b);
}
current_start = current_end;
if !((current_start as usize + 1) < packet_data_len) {
break;
}
current_packet_len = packet_data[current_start as usize + 1] + 6;
current_end = current_start + current_packet_len;
}
let result = String::from_utf8(vec);
if result.is_err() {
return Err(PacketError::CorruptedMessage)
}
Ok(result.unwrap())
}
}

Стефан качи решение на 02.12.2019 18:13 (преди почти 6 години)

use std::fmt;
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 => f.write_str("Invalid packet!"),
PacketError::InvalidChecksum => f.write_str("The checksum is invalid!"),
PacketError::UnknownProtocolVersion => f.write_str("The version of the protocol is unknown!"),
PacketError::CorruptedMessage => f.write_str("The message was corrupted!"),
}
}
}
impl std::error::Error for PacketError {}
#[derive(PartialEq, Debug)]
pub struct Packet<'a> {
version: u8,
message_size: u8,
message: &'a [u8],
checksum: u32
}
impl<'a> Packet<'a> {
pub fn from_source(source: &'a [u8], size: u8) -> (Self, &'a [u8]) {
if size == 0 {
panic!("Cannot create packet with size 0!");
}
let mut message_size = size;
if source.len() < message_size as usize {
message_size = source.len().try_into().unwrap();
}
let mut checksum :u32 = 0;
let (message,rest) = source.split_at(message_size as usize);
for &m in message {
checksum += m as u32;
}
(Packet {version: 1, message_size, message, checksum}, rest)
}
pub fn payload(&self) -> &[u8] {
self.message
}
pub fn serialize(&self) -> Vec<u8> {
let mut serialized : Vec<u8> = Vec::new();
serialized.push(self.version);
serialized.push(self.message_size);
for &b in self.payload() {
serialized.push(b);
}
for &c in &(self.checksum).to_be_bytes() {
serialized.push(c);
}
serialized
}
pub fn deserialize(bytes: &'a [u8]) -> Result<(Packet, &'a [u8]), PacketError> {
let bytes_len = bytes.len();
if bytes_len <= 6 { // version + size + 0 data + checksum
return Err(PacketError::InvalidPacket)
}
if bytes[0] != 1 {
return Err(PacketError::UnknownProtocolVersion)
}
let msg_size = bytes[1];
let expected_packet_len = 6 + msg_size;
if bytes_len < expected_packet_len as usize {
return Err(PacketError::InvalidPacket)
}else{
let k = 2 + msg_size as usize;
let payload = &bytes[2..k];
let checksum = as_u32_to_be(&bytes[k..k+4]);
let rest = &bytes[k+4..];
let mut expected_checksum :u32 = 0;
for &m in payload {
expected_checksum += m as u32;
}
if expected_checksum != checksum{
return Err(PacketError::InvalidChecksum)
}
let (packet,_) = Packet::from_source(payload,msg_size);
Ok((packet,rest))
}
}
}
pub fn as_u32_to_be(array: &[u8]) -> u32 {
((array[0] as u32) << 24) +
((array[1] as u32) << 16) +
((array[2] as u32) << 8) +
((array[3] as u32) << 0)
}
pub struct PacketSerializer<'a> {
packets:Vec<Packet<'a>>,
}
impl<'a> PacketSerializer<'a>{
pub fn new(packets:Vec<Packet<'a>>) -> Self {
PacketSerializer {packets}
}
}
impl<'b> Iterator for PacketSerializer<'b> {
type Item = Packet<'b>;
fn next(&mut self) -> Option<Self::Item> {
self.packets.pop()
}

Единия от бъговете ти идва тук -- метода pop премахва елемент от края на вектора, а метода push добавя елементи към края. Ти използваш тези два метода, което кара вектора ти да се държи като стек, и резултата излиза наобратно.

Тук би могъл да викнеш .remove(0) и да минат още 3 теста. Ако искаш да поддържаш ефективност, можеш да използваш std::collections::VecDeque вместо вектор.

}
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 {
let mut packets :Vec<Packet> = Vec::new();
let self_as_bytes = self.as_bytes();
let self_len = self.len();
-
+ if self_len == 0 {
+ return PacketSerializer::new(packets);
+ }
let mut real_packet_size = packet_size;
if self_len < packet_size as usize {
real_packet_size = self_len.try_into().unwrap();
}
let mut start = 0;
let mut end = real_packet_size;
while (end as usize) <= self_len {
let current_payload = self_as_bytes.get(start as usize .. end as usize).unwrap();
let (packet,_) = Packet::from_source(current_payload,real_packet_size);
packets.push(packet);
start = end;
if !((start as usize + 1) < self_len) {
break;
}
if ((start + real_packet_size) as usize) > self_len {
real_packet_size = (self_len - start as usize).try_into().unwrap();
}
end = start + real_packet_size;
}

Тук е другия ти бъг, но честно казано не мога да го намеря, понеже кода е твърде сложен :). Off-by-one грешка е, което е често срещана ситуация. Наистина, най-лесния начин да избягваш бъгове е да пишеш прост код, но може да е предизвикателно, admittedly.

Целта на Packet::from_source беше да се използва итеративно. Ако вместо да пазиш вектор от пакети, си пазеше slice-а от байтове, можеше да си улесниш доста живота като го викаш в цикъл върху това, което връща като "остатък". Ето моята имплементация:

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)
    }
}

Оттам нататък, .to_packets се свежда просто до:

fn to_packets(&self, packet_size: u8) -> PacketSerializer {
    PacketSerializer { source: self.as_bytes(), packet_size }
}

Дори да предпочиташ да си направиш вектор, който да подадеш на итератора, това пак щеше да може да се направи със същата логика преместена в .to_packets.

Разбира се, не искаме всички да пишат едни и същи решения, и не очакваме да "познаете" как сме решили ние проблема. Но в някои отношения е добре да се вгледаш в инструментите, които ти дава интерфейса и да се възползваш от тях. Ти конструираш пакет, но остатъка който се връща го игнорираш напълно. Опитваш се да изчислиш "истинския" packet size, но метода .from_source вече го прави.

PacketSerializer::new(packets)
}
fn to_packet_data(&self, packet_size: u8) -> Vec<u8> {
let mut packet_data:Vec<u8> = Vec::new();
let mut packet_serializer = self.to_packets(packet_size);
while let Some(entry) = packet_serializer.next() {
for m in entry.serialize(){
packet_data.push(m);
}
}
packet_data
}
fn from_packet_data(packet_data: &[u8]) -> Result<Self, PacketError> {
let mut vec:Vec<u8> = Vec::new();
let packet_data_len = packet_data.len();
if packet_data_len <= 6 {
return Err(PacketError::InvalidPacket)
}
let mut current_packet_len = packet_data[1] + 6;
let mut current_end = current_packet_len;
let mut current_start = 0;
while (current_end as usize) <= packet_data_len {
let (current_packet,_) = Packet::deserialize(&packet_data[current_start as usize ..current_end as usize])?;
let packet_payload = current_packet.payload();
for &b in packet_payload {
vec.push(b);
}
current_start = current_end;
if !((current_start as usize + 1) < packet_data_len) {
break;
}
current_packet_len = packet_data[current_start as usize + 1] + 6;
current_end = current_start + current_packet_len;
}
let result = String::from_utf8(vec);
if result.is_err() {
return Err(PacketError::CorruptedMessage)
}
Ok(result.unwrap())
}
}

Имаш някои бъгове, но повечето код работи. Главния проблем според мен е, че не си се възползвал от функциите, които вече имаш, било то от собствения си код, било то от стандартната библиотека. Опитал си се да решиш много проблеми "на ръка" които можеше да решиш по-лесно. Огледай останалите решения за вдъхновение.