From 500ba7267a7cc05268dad9e2131e5a5a07cd7322 Mon Sep 17 00:00:00 2001 From: Danil Sidoruk Date: Fri, 22 Mar 2024 22:36:27 +0300 Subject: [PATCH] Rewrite parse functions --- tests/parse_test.cpp | 48 +-- tftp_common/details/packets.hpp | 12 - tftp_common/details/parsers.hpp | 597 ++++++++++++++++++-------------- 3 files changed, 352 insertions(+), 305 deletions(-) diff --git a/tests/parse_test.cpp b/tests/parse_test.cpp index 5f8b6be..be02256 100644 --- a/tests/parse_test.cpp +++ b/tests/parse_test.cpp @@ -14,15 +14,15 @@ TEST(Request, Parse) { 0x6e, 0x65, 0x74, 0x61, 0x73, 0x63, 0x69, 0x69, 0x00}; auto Length = sizeof(PacketBytes) / sizeof(std::uint8_t); - Request Packet; - auto [success, bytesRead] = parse(PacketBytes, Length, Packet); + auto Res = Parser::parse(PacketBytes, Length); + auto [Packet, BytesRead] = Res.get(); ASSERT_EQ(Packet.getType(), types::ReadRequest); ASSERT_EQ(Packet.getFilename(), "/srv/tftp/ReadFile"); ASSERT_EQ(Packet.getMode(), "netascii"); - ASSERT_EQ(success, true); - ASSERT_EQ(bytesRead, Length); + ASSERT_EQ(Res.isSuccess(), true); + ASSERT_EQ(BytesRead, Length); } /// Test that Request packet with options parsing is going fine @@ -50,8 +50,8 @@ TEST(Request, OptionParse) { 0x7A, 0x31, 0x67, 0x53, 0x30, 0x58, 0x78, 0x4A, 0x57, 0x33, 0x00}; auto Length = sizeof(PacketBytes) / sizeof(std::uint8_t); - Request Packet; - auto [success, bytesRead] = parse(PacketBytes, Length, Packet); + auto Res = Parser::parse(PacketBytes, Length); + auto [Packet, BytesRead] = Res.get(); ASSERT_EQ(Packet.getType(), types::ReadRequest); ASSERT_EQ(Packet.getFilename(), "/srv/tftp/ReadFile"); @@ -64,8 +64,8 @@ TEST(Request, OptionParse) { ASSERT_EQ(Packet.getOptionValue(Idx), OptionsValues[Idx]); } - ASSERT_EQ(success, true); - ASSERT_EQ(bytesRead, Length); + ASSERT_EQ(Res.isSuccess(), true); + ASSERT_EQ(BytesRead, Length); } /// Test that Data packet parsing is going fine @@ -79,8 +79,8 @@ TEST(Data, Parse) { 0x2e, 0x2e, 0x0d, 0x0a}; auto Length = sizeof(PacketBytes) / sizeof(std::uint8_t); - Data Packet; - auto [success, bytesRead] = parse(PacketBytes, Length, Packet); + auto Res = Parser::parse(PacketBytes, Length); + auto [Packet, BytesRead] = Res.get(); ASSERT_EQ(Packet.getType(), types::DataPacket); ASSERT_EQ(Packet.getBlock(), 0x01); @@ -91,8 +91,8 @@ TEST(Data, Parse) { ASSERT_EQ(Data[Idx], PacketBytes[BaseOffset + Idx]); } - ASSERT_EQ(success, true); - ASSERT_EQ(bytesRead, Length); + ASSERT_EQ(Res.isSuccess(), true); + ASSERT_EQ(BytesRead, Length); } /// Test that Acknowledgment packet parsing is going fine @@ -103,14 +103,14 @@ TEST(Acknowledgment, Parse) { 0x00, 0x01}; auto Length = sizeof(PacketBytes) / sizeof(std::uint8_t); - Acknowledgment Packet; - auto [success, bytesRead] = parse(PacketBytes, Length, Packet); + auto Res = Parser::parse(PacketBytes, Length); + auto [Packet, BytesRead] = Res.get(); ASSERT_EQ(Packet.getType(), types::AcknowledgmentPacket); ASSERT_EQ(Packet.getBlock(), 0x01); - ASSERT_EQ(success, true); - ASSERT_EQ(bytesRead, Length); + ASSERT_EQ(Res.isSuccess(), true); + ASSERT_EQ(BytesRead, Length); } /// Test that Error packet parsing is going fine @@ -124,15 +124,15 @@ TEST(Error, Parse) { 0x00}; auto Length = sizeof(PacketBytes) / sizeof(std::uint8_t); - Error Packet; - auto [success, bytesRead] = parse(PacketBytes, Length, Packet); + auto Res = Parser::parse(PacketBytes, Length); + auto [Packet, BytesRead] = Res.get(); ASSERT_EQ(Packet.getType(), types::ErrorPacket); ASSERT_EQ(Packet.getErrorCode(), 0x01); ASSERT_EQ(Packet.getErrorMessage(), "File not found"); - ASSERT_EQ(success, true); - ASSERT_EQ(bytesRead, Length); + ASSERT_EQ(Res.isSuccess(), true); + ASSERT_EQ(BytesRead, Length); } /// Test that Option Acknowledgment packet parsing is going fine @@ -155,8 +155,8 @@ TEST(OptionAcknowledgment, Parse) { 0x7A, 0x31, 0x67, 0x53, 0x30, 0x58, 0x78, 0x4A, 0x57, 0x33, 0x00}; auto Length = sizeof(PacketBytes) / sizeof(std::uint8_t); - OptionAcknowledgment Packet; - auto [success, bytesRead] = parse(PacketBytes, Length, Packet); + auto Res = Parser::parse(PacketBytes, Length); + auto [Packet, BytesRead] = Res.get(); ASSERT_EQ(Packet.getType(), types::OptionAcknowledgmentPacket); @@ -166,8 +166,8 @@ TEST(OptionAcknowledgment, Parse) { ASSERT_EQ(Packet.getOptionValue(Key), Value); } - ASSERT_EQ(success, true); - ASSERT_EQ(bytesRead, Length); + ASSERT_EQ(Res.isSuccess(), true); + ASSERT_EQ(BytesRead, Length); } int main(int argc, char **argv) { diff --git a/tftp_common/details/packets.hpp b/tftp_common/details/packets.hpp index 5638c52..f7884dd 100644 --- a/tftp_common/details/packets.hpp +++ b/tftp_common/details/packets.hpp @@ -15,8 +15,6 @@ namespace tftp_common::packets { -struct ParseResult; - namespace types { /// Trivial File Transfer Protocol packet type @@ -153,8 +151,6 @@ class Request final { } private: - friend ParseResult parse(const std::uint8_t *Buffer, std::size_t Len, Request &Packet); - std::uint16_t Type_; std::string Filename; std::string Mode; @@ -208,8 +204,6 @@ class Data final { const std::vector &getData() const noexcept { return DataBuffer; } private: - friend ParseResult parse(const std::uint8_t *Buffer, std::size_t Len, Data &Packet); - std::uint16_t Type_ = types::DataPacket; std::uint16_t Block; std::vector DataBuffer; @@ -243,8 +237,6 @@ class Acknowledgment final { } private: - friend ParseResult parse(const std::uint8_t *Buffer, std::size_t Len, Acknowledgment &Packet); - std::uint16_t Type_ = types::AcknowledgmentPacket; std::uint16_t Block; }; @@ -290,8 +282,6 @@ class Error final { } private: - friend ParseResult parse(const std::uint8_t *Buffer, std::size_t Len, Error &Packet); - std::uint16_t Type_ = types::ErrorPacket; std::uint16_t ErrorCode; std::string ErrorMessage; @@ -352,8 +342,6 @@ class OptionAcknowledgment final { std::string_view getOptionValue(const std::string &OptionName) const noexcept { return Options.at(OptionName); } private: - friend ParseResult parse(const std::uint8_t *Buffer, std::size_t Len, OptionAcknowledgment &Packet); - std::uint16_t Type_ = types::OptionAcknowledgmentPacket; // According to the RFC, the order in which options are specified is not significant, so it's fine std::unordered_map Options; diff --git a/tftp_common/details/parsers.hpp b/tftp_common/details/parsers.hpp index af437ce..a7cf534 100644 --- a/tftp_common/details/parsers.hpp +++ b/tftp_common/details/parsers.hpp @@ -1,306 +1,365 @@ #pragma once #include "packets.hpp" +#include +#include namespace tftp_common::packets { /// The result of parsing a single packet +template struct ParseResult { - /// if the parsing was successful - bool Success; - /// count of bytes read + T Packet; std::size_t BytesRead; }; -/// Parse read/write request packet from buffer converting all fields to host byte order -/// @param[Buffer] Assumptions: \p Buffer is not a nullptr, it's size is greater or equal than \p Len -/// @param[Len] Assumptions: \p Len is greater than zero -/// @n If parsing wasn't successful, \p Packet remains in valid but unspecified state -ParseResult parse(const std::uint8_t *Buffer, std::size_t Len, Request &Packet) { - assert(Buffer != nullptr); - assert(Len > 0); - - std::string Name; - std::string Value; - std::size_t Step = 0; - std::size_t BytesRead = 0; - for (std::size_t Idx = 0; Idx != Len; ++Idx) { - const auto Byte = Buffer[Idx]; - BytesRead++; - - switch (Step) { - // Opcode (2 bytes) - case 0: - Packet.Type_ = std::uint16_t(Byte) << 0; - Step++; - break; - case 1: - Packet.Type_ |= std::uint16_t(Byte) << 8; - Packet.Type_ = ntohs(Packet.Type_); - if (Packet.Type_ != types::ReadRequest && Packet.Type_ != types::WriteRequest) { - Step = 0; - continue; - } - Step++; - break; - // Filename - case 2: - if (Byte == 0u) { +/// Return type of `parse` functions +template +struct ParseReturn : public std::variant, std::nullopt_t> { + using base = std::variant, std::nullopt_t>; + using base::base; + + ParseResult get() const noexcept { + return std::get>(*this); + } + + bool isSuccess() const noexcept { + return !std::holds_alternative(*this); + } +}; + +template +struct Parser { + using PacketType = T; +}; + +template <> +struct Parser { + /// Parse read/write request packet from buffer converting all fields to host byte order + /// @param[Buffer] Assumptions: \p Buffer is not a nullptr, it's size is greater or equal than \p Len + /// @param[Len] Assumptions: \p Len is greater than zero + /// @n If parsing wasn't successful, \p Packet remains in valid but unspecified state + static ParseReturn parse(const std::uint8_t *Buffer, std::size_t Len) { + assert(Buffer != nullptr); + assert(Len > 0); + + std::uint16_t Type_; + std::string Filename; + std::string Mode; + std::vector OptionsNames; + std::vector OptionsValues; + std::string Name; + std::string Value; + + std::size_t Step = 0; + std::size_t BytesRead = 0; + for (std::size_t Idx = 0; Idx != Len; ++Idx) { + const auto Byte = Buffer[Idx]; + BytesRead++; + + switch (Step) { + // Opcode (2 bytes) + case 0: + Type_ = std::uint16_t(Byte) << 0; Step++; - } else { - Packet.Filename.push_back(Byte); - } - break; - // Mode - case 3: - if (Byte == 0u) { - if (Idx == Len - 1) { - return ParseResult{true, BytesRead}; + break; + case 1: + Type_ |= std::uint16_t(Byte) << 8; + Type_ = ntohs(Type_); + if (Type_ != types::ReadRequest && Type_ != types::WriteRequest) { + Step = 0; + continue; } Step++; - } else { - Packet.Mode.push_back(Byte); + break; + // Filename + case 2: + if (Byte == 0u) { + Step++; + } else { + Filename.push_back(Byte); + } + break; + // Mode + case 3: + if (Byte == 0u) { + if (Idx == Len - 1) { + return ParseResult{Request{(types::Type) Type_, std::move(Filename), std::move(Mode)}, BytesRead}; + } + Step++; + } else { + Mode.push_back(Byte); + } + break; + // Option name + case 4: + if (Byte == 0u) { + OptionsNames.push_back(std::move(Name)); + Step++; + } else { + Name.push_back(Byte); + } + break; + // Option value + case 5: + if (Byte == 0u) { + OptionsValues.push_back(std::move(Value)); + + if (Idx == Len - 1) { + return ParseResult{Request{(types::Type) Type_, std::move(Filename), std::move(Mode), std::move(OptionsNames), std::move(OptionsValues)}, BytesRead}; + } + Step--; + } else { + Value.push_back(Byte); + } + break; + default: + assert(false); } - break; - // Option name - case 4: - if (Byte == 0u) { - Packet.OptionsNames.push_back(std::move(Name)); + } + return {std::nullopt}; + } +}; + +template <> +struct Parser { + /// Parse data packet from buffer converting all fields to host byte order + /// @param[Buffer] Assumptions: \p Buffer is not a nullptr, it's size is greater or equal than \p Len + /// @param[Len] Assumptions: \p Len is greater than zero + /// @n If parsing wasn't successful, \p Packet remains in valid but unspecified state + static ParseReturn parse(const std::uint8_t *Buffer, std::size_t Len) { + assert(Buffer != nullptr); + assert(Len > 0); + + std::uint16_t Type_; + std::uint16_t Block; + std::vector DataBuffer; + + + std::size_t Step = 0; + std::size_t BytesRead = 0; + for (std::size_t Idx = 0; Idx != Len; ++Idx) { + const auto Byte = Buffer[Idx]; + BytesRead++; + + switch (Step) { + // Opcode (2 bytes) + case 0: + Type_ = std::uint16_t(Byte) << 0; Step++; - } else { - Name.push_back(Byte); - } - break; - // Option value - case 5: - if (Byte == 0u) { - Packet.OptionsValues.push_back(std::move(Value)); + break; + case 1: + Type_ |= std::uint16_t(Byte) << 8; + Type_ = ntohs(Type_); + if (Type_ != types::DataPacket) { + Step = 0; + continue; + } + Step++; + break; + // Block # (2 bytes) + case 2: + Block = std::uint16_t(Byte) << 0; + Step++; + break; + case 3: + Block |= std::uint16_t(Byte) << 8; + Block = ntohs(Block); + Step++; + break; + // buffer + case 4: + DataBuffer.push_back(Byte); if (Idx == Len - 1) { - return ParseResult{true, BytesRead}; + return ParseResult{Data{Block, std::move(DataBuffer)}, BytesRead}; } - Step--; - } else { - Value.push_back(Byte); + + break; + default: + assert(false); } - break; - default: - assert(false); } + return {std::nullopt}; } - return ParseResult{false, BytesRead}; -} - -/// Parse data packet from buffer converting all fields to host byte order -/// @param[Buffer] Assumptions: \p Buffer is not a nullptr, it's size is greater or equal than \p Len -/// @param[Len] Assumptions: \p Len is greater than zero -/// @n If parsing wasn't successful, \p Packet remains in valid but unspecified state -ParseResult parse(const std::uint8_t *Buffer, std::size_t Len, Data &Packet) { - assert(Buffer != nullptr); - assert(Len > 0); - - std::size_t Step = 0; - std::size_t BytesRead = 0; - for (std::size_t Idx = 0; Idx != Len; ++Idx) { - const auto Byte = Buffer[Idx]; - BytesRead++; - - switch (Step) { - // Opcode (2 bytes) - case 0: - Packet.Type_ = std::uint16_t(Byte) << 0; - Step++; - break; - case 1: - Packet.Type_ |= std::uint16_t(Byte) << 8; - Packet.Type_ = ntohs(Packet.Type_); - if (Packet.Type_ != types::DataPacket) { - Step = 0; - continue; - } - Step++; - break; - // Block # (2 bytes) - case 2: - Packet.Block = std::uint16_t(Byte) << 0; - Step++; - break; - case 3: - Packet.Block |= std::uint16_t(Byte) << 8; - Packet.Block = ntohs(Packet.Block); - Step++; - break; - // buffer - case 4: - Packet.DataBuffer.push_back(Byte); - - if (Idx == Len - 1) { - return ParseResult{true, BytesRead}; - } +}; - break; - default: - assert(false); - } - } - return ParseResult{false, BytesRead}; -} - -/// Parse acknowledgment packet from buffer converting all fields to host byte order -/// @param[Buffer] Assumptions: \p Buffer is not a nullptr, it's size is greater or equal than \p Len -/// @param[Len] Assumptions: \p Len is greater than zero -/// @n If parsing wasn't successful, \p Packet remains in valid but unspecified state -ParseResult parse(const std::uint8_t *Buffer, std::size_t Len, Acknowledgment &Packet) { - assert(Buffer != nullptr); - assert(Len > 0); - - std::size_t Step = 0; - std::size_t BytesRead = 0; - for (std::size_t Idx = 0; Idx != Len; ++Idx) { - const auto Byte = Buffer[Idx]; - BytesRead++; - - switch (Step) { - // Opcode (2 bytes) - case 0: - Packet.Type_ = std::uint16_t(Byte) << 0; - Step++; - break; - case 1: - Packet.Type_ |= std::uint16_t(Byte) << 8; - Packet.Type_ = ntohs(Packet.Type_); - if (Packet.Type_ != types::AcknowledgmentPacket) { - Step = 0; - continue; +template <> +struct Parser { + /// Parse acknowledgment packet from buffer converting all fields to host byte order + /// @param[Buffer] Assumptions: \p Buffer is not a nullptr, it's size is greater or equal than \p Len + /// @param[Len] Assumptions: \p Len is greater than zero + /// @n If parsing wasn't successful, \p Packet remains in valid but unspecified state + static ParseReturn parse(const std::uint8_t *Buffer, std::size_t Len) { + assert(Buffer != nullptr); + assert(Len > 0); + + std::uint16_t Type_; + std::uint16_t Block; + + std::size_t Step = 0; + std::size_t BytesRead = 0; + for (std::size_t Idx = 0; Idx != Len; ++Idx) { + const auto Byte = Buffer[Idx]; + BytesRead++; + + switch (Step) { + // Opcode (2 bytes) + case 0: + Type_ = std::uint16_t(Byte) << 0; + Step++; + break; + case 1: + Type_ |= std::uint16_t(Byte) << 8; + Type_ = ntohs(Type_); + if (Type_ != types::AcknowledgmentPacket) { + Step = 0; + continue; + } + Step++; + break; + // Block # (2 bytes) + case 2: + Block = std::uint16_t(Byte) << 0; + Step++; + break; + case 3: + Block |= std::uint16_t(Byte) << 8; + Block = ntohs(Block); + return ParseResult{Acknowledgment{Block}, BytesRead}; + default: + assert(false); } - Step++; - break; - // Block # (2 bytes) - case 2: - Packet.Block = std::uint16_t(Byte) << 0; - Step++; - break; - case 3: - Packet.Block |= std::uint16_t(Byte) << 8; - Packet.Block = ntohs(Packet.Block); - return ParseResult{true, BytesRead}; - default: - assert(false); } + return {std::nullopt}; } - return ParseResult{false, BytesRead}; -} - -/// Parse error packet from buffer converting all fields to host byte order -/// @param[Buffer] Assumptions: \p Buffer is not a nullptr, it's size is greater or equal than \p Len -/// @param[Len] Assumptions: \p Len is greater than zero -/// @n If parsing wasn't successful, \p Packet remains in valid but unspecified state -ParseResult parse(const std::uint8_t *Buffer, std::size_t Len, Error &Packet) { - assert(Buffer != nullptr); - assert(Len > 0); - - std::size_t Step = 0; - std::size_t BytesRead = 0; - for (std::size_t Idx = 0; Idx != Len; ++Idx) { - const auto Byte = Buffer[Idx]; - BytesRead++; - - switch (Step) { - // Opcode (2 bytes) - case 0: - Packet.Type_ = std::uint16_t(Byte) << 0; - Step++; - break; - case 1: - Packet.Type_ |= std::uint16_t(Byte) << 8; - Packet.Type_ = ntohs(Packet.Type_); - if (Packet.Type_ != types::ErrorPacket) { - Step = 0; - continue; - } - Step++; - break; - // ErrorCode (2 bytes) - case 2: - Packet.ErrorCode = std::uint16_t(Byte) << 0; - Step++; - break; - case 3: - Packet.ErrorCode |= std::uint16_t(Byte) << 8; - Packet.ErrorCode = ntohs(Packet.ErrorCode); - Step++; - break; - // ErrorMessage - case 4: - if (Byte == 0u) { - return ParseResult{true, BytesRead}; - } else { - Packet.ErrorMessage.push_back(Byte); +}; + +template <> +struct Parser { + /// Parse error packet from buffer converting all fields to host byte order + /// @param[Buffer] Assumptions: \p Buffer is not a nullptr, it's size is greater or equal than \p Len + /// @param[Len] Assumptions: \p Len is greater than zero + /// @n If parsing wasn't successful, \p Packet remains in valid but unspecified state + static ParseReturn parse(const std::uint8_t *Buffer, std::size_t Len) { + assert(Buffer != nullptr); + assert(Len > 0); + + std::uint16_t Type_ = types::ErrorPacket; + std::uint16_t ErrorCode; + std::string ErrorMessage; + + std::size_t Step = 0; + std::size_t BytesRead = 0; + for (std::size_t Idx = 0; Idx != Len; ++Idx) { + const auto Byte = Buffer[Idx]; + BytesRead++; + + switch (Step) { + // Opcode (2 bytes) + case 0: + Type_ = std::uint16_t(Byte) << 0; + Step++; + break; + case 1: + Type_ |= std::uint16_t(Byte) << 8; + Type_ = ntohs(Type_); + if (Type_ != types::ErrorPacket) { + Step = 0; + continue; + } + Step++; + break; + // ErrorCode (2 bytes) + case 2: + ErrorCode = std::uint16_t(Byte) << 0; + Step++; + break; + case 3: + ErrorCode |= std::uint16_t(Byte) << 8; + ErrorCode = ntohs(ErrorCode); + Step++; + break; + // ErrorMessage + case 4: + if (Byte == 0u) { + return ParseResult{Error{ErrorCode, ErrorMessage}, BytesRead}; + } else { + ErrorMessage.push_back(Byte); + } + break; + default: + assert(false); } - break; - default: - assert(false); } + return {std::nullopt}; } - return ParseResult{false, BytesRead}; -} - -/// Parse error packet from buffer converting all fields to host byte order -/// @param[Buffer] Assumptions: \p Buffer is not a nullptr, it's size is greater or equal than \p Len -/// @param[Len] Assumptions: \p Len is greater than zero -/// @n If parsing wasn't successful, \p Packet remains in valid but unspecified state -ParseResult parse(const std::uint8_t *Buffer, std::size_t Len, OptionAcknowledgment &Packet) { - assert(Buffer != nullptr); - assert(Len > 0); - - std::string Name; - std::string Value; - std::size_t Step = 0; - std::size_t BytesRead = 0; - for (std::size_t Idx = 0; Idx != Len; ++Idx) { - const auto Byte = Buffer[Idx]; - BytesRead++; - - switch (Step) { - // Opcode (2 bytes) - case 0: - Packet.Type_ = std::uint16_t(Byte) << 0; - Step++; - break; - case 1: - Packet.Type_ |= std::uint16_t(Byte) << 8; - Packet.Type_ = ntohs(Packet.Type_); - if (Packet.Type_ != types::OptionAcknowledgmentPacket) { - Step = 0; - continue; - } - Step++; - break; - // Option name - case 2: - if (Byte == 0u) +}; + +template <> +struct Parser { + + /// Parse error packet from buffer converting all fields to host byte order + /// @param[Buffer] Assumptions: \p Buffer is not a nullptr, it's size is greater or equal than \p Len + /// @param[Len] Assumptions: \p Len is greater than zero + /// @n If parsing wasn't successful, \p Packet remains in valid but unspecified state + static ParseReturn parse(const std::uint8_t *Buffer, std::size_t Len) { + assert(Buffer != nullptr); + assert(Len > 0); + + std::uint16_t Type_; + // According to the RFC, the order in which options are specified is not significant, so it's fine + std::unordered_map Options; + std::string Name; + std::string Value; + + std::size_t Step = 0; + std::size_t BytesRead = 0; + for (std::size_t Idx = 0; Idx != Len; ++Idx) { + const auto Byte = Buffer[Idx]; + BytesRead++; + + switch (Step) { + // Opcode (2 bytes) + case 0: + Type_ = std::uint16_t(Byte) << 0; Step++; - else - Name.push_back(Byte); - break; - // Option value - case 3: - if (Byte == 0u) { - Packet.Options.emplace(std::move(Name), std::move(Value)); + break; + case 1: + Type_ |= std::uint16_t(Byte) << 8; + Type_ = ntohs(Type_); + if (Type_ != types::OptionAcknowledgmentPacket) { + Step = 0; + continue; + } + Step++; + break; + // Option name + case 2: + if (Byte == 0u) + Step++; + else + Name.push_back(Byte); + break; + // Option value + case 3: + if (Byte == 0u) { + Options.emplace(std::move(Name), std::move(Value)); - if (Idx == Len - 1) { - return ParseResult{true, BytesRead}; + if (Idx == Len - 1) { + return ParseResult{OptionAcknowledgment{std::move(Options)}, BytesRead}; + } + Step--; + } else { + Value.push_back(Byte); } - Step--; - } else { - Value.push_back(Byte); + break; + default: + assert(false); } - break; - default: - assert(false); } + return {std::nullopt}; } - return ParseResult{false, BytesRead}; -} +}; } // namespace tftp_common::packets