Skip to content

Commit

Permalink
feat: add octet string parser.
Browse files Browse the repository at this point in the history
  • Loading branch information
ashigeru committed Aug 9, 2024
1 parent 102f71d commit 922e37d
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 0 deletions.
11 changes: 11 additions & 0 deletions include/takatori/value/octet.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "value_kind.h"
#include "simple_value.h"

#include <takatori/util/either.h>
#include <takatori/util/meta_type.h>

namespace takatori::value {
Expand Down Expand Up @@ -93,6 +94,16 @@ bool operator!=(octet const& a, octet const& b) noexcept;
*/
std::ostream& operator<<(std::ostream& out, octet const& value);

/**
* @brief parses the given literal as an octet string.
* @details The input literal must be a sequence of __octet values__, that is consists of two hexadecimal digits.
* @param literal the source literal
* @param buffer the buffer to store the parsed octet in its tail
* @return the number of parsed octets
* @return an error message if the conversion was failed
*/
[[nodiscard]] util::either<std::string, std::size_t> parse_octet(std::string_view literal, std::string& buffer);

/**
* @brief type_of for octet.
*/
Expand Down
46 changes: 46 additions & 0 deletions src/takatori/value/octet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,50 @@ std::ostream& operator<<(std::ostream& out, octet const& value) {
return out << ")";
}

util::either<std::string, std::size_t> parse_octet(std::string_view literal, std::string& buffer) {
using std::string_literals::operator""s;
std::size_t hex_digit_count = 0;
for (auto c : literal) {
if (c == ' ') {
continue;
}
if (('0' <= c && c <= '9') || ('A' <= c && c <= 'F') || ('a' <= c && c <= 'f')) {
++hex_digit_count;
continue;
}
return "invalid character in octet string"s;
}
if (hex_digit_count % 2 != 0) {
return "unexpected end of octet string (odd hex digits)"s;
}
std::size_t octet_count = hex_digit_count / 2;

std::size_t offset { buffer.size() };
buffer.resize(offset + octet_count);
std::uint8_t octet {};
bool high = true;
for (auto c : literal) {
if (c == ' ') {
continue;
}
if ('0' <= c && c <= '9') {
octet |= static_cast<std::uint8_t>(c - '0');
} else if ('A' <= c && c <= 'F') {
octet |= static_cast<std::uint8_t>(c - 'A' + 10);
} else if ('a' <= c && c <= 'f') {
octet |= static_cast<std::uint8_t>(c - 'a' + 10);
}
if (high) {
high = false;
octet = static_cast<std::uint8_t>(octet << 4U);
continue;
}
high = true;
buffer[offset] = static_cast<std::string::value_type>(octet);
octet = 0U;
++offset;
}
return octet_count;
}

} // namespace takatori::value
38 changes: 38 additions & 0 deletions test/takatori/value/octet_value_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ namespace takatori::value {

class octet_value_test : public ::testing::Test {};

using std::string_literals::operator""s;

static_assert(octet::tag == value_kind::octet);
static_assert(std::is_same_v<type_of_t<octet::tag>, octet>);

Expand Down Expand Up @@ -45,4 +47,40 @@ TEST_F(octet_value_test, output) {
std::cout << v << std::endl;
}

TEST_F(octet_value_test, parse_octet) {
std::string output {};
auto result = parse_octet("01 23 45 67 89 ab cd ef", output);
ASSERT_TRUE(result);
EXPECT_EQ(result.value(), 8);
EXPECT_EQ(output, "\x01\x23\x45\x67\x89\xab\xcd\xef"s) << output;
}

TEST_F(octet_value_test, parse_octet_empty) {
std::string output {};
auto result = parse_octet("", output);
ASSERT_TRUE(result);
EXPECT_EQ(result.value(), 0);
EXPECT_EQ(output, ""s) << output;
}

TEST_F(octet_value_test, parse_octet_append) {
std::string output { "ABC" };
auto result = parse_octet(" 58 59 5A ", output);
ASSERT_TRUE(result);
EXPECT_EQ(result.value(), 3);
EXPECT_EQ(output, "ABCXYZ"s) << output;
}

TEST_F(octet_value_test, parse_octet_unexpected_char) {
std::string output {};
auto result = parse_octet("X", output);
ASSERT_FALSE(result);
}

TEST_F(octet_value_test, parse_octet_odd) {
std::string output {};
auto result = parse_octet("01 23 4", output);
ASSERT_FALSE(result);
}

} // namespace takatori::value

0 comments on commit 922e37d

Please sign in to comment.