Skip to content

Commit 78cc6db

Browse files
committed
Add ObisId::from_string
1 parent 3d8c8e1 commit 78cc6db

2 files changed

Lines changed: 68 additions & 0 deletions

File tree

src/dlms_parser/obis_id.h

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include <compare>
55
#include <cstdio>
66
#include <cstdint>
7+
#include <optional>
78
#include <span>
89
#include <string_view>
910
#include <algorithm>
@@ -21,6 +22,40 @@ struct ObisId final {
2122

2223
[[nodiscard]] bool empty() const { return v == std::array<uint8_t, 6>{}; }
2324

25+
[[nodiscard]] static std::optional<ObisId> from_string(const std::string_view str) noexcept {
26+
std::array<uint8_t, 6> values{};
27+
size_t count = 0;
28+
size_t pos = 0;
29+
30+
while (pos < str.size()) {
31+
if (count == values.size()) return std::nullopt;
32+
if (str[pos] < '0' || str[pos] > '9') return std::nullopt;
33+
34+
unsigned value = 0;
35+
while (pos < str.size() && str[pos] >= '0' && str[pos] <= '9') {
36+
value = value * 10U + static_cast<unsigned>(str[pos] - '0');
37+
if (value > 255U) return std::nullopt;
38+
++pos;
39+
}
40+
41+
values[count++] = static_cast<uint8_t>(value);
42+
43+
if (pos == str.size()) break;
44+
if (str[pos] != '.' && str[pos] != '-') return std::nullopt;
45+
46+
++pos;
47+
if (pos == str.size()) return std::nullopt;
48+
}
49+
50+
if (count == 5) {
51+
values[5] = 255;
52+
} else if (count != values.size()) {
53+
return std::nullopt;
54+
}
55+
56+
return ObisId(values[0], values[1], values[2], values[3], values[4], values[5]);
57+
}
58+
2459
[[nodiscard]] std::string_view to_string(std::span<char> buffer) const {
2560
if (buffer.empty()) return {};
2661
const int len = snprintf(buffer.data(), buffer.size(), "%u.%u.%u.%u.%u.%u", v[0], v[1], v[2], v[3], v[4], v[5]);

tests/test_obis_id.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#include <doctest.h>
2+
3+
#include "tests/log_fixture.h"
4+
#include "dlms_parser/obis_id.h"
5+
6+
using namespace dlms_parser;
7+
8+
TEST_CASE_FIXTURE(LogFixture, "OBIS ID parsing") {
9+
SUBCASE("dot-separated six-byte form") {
10+
const auto parsed = ObisId::from_string("0.0.96.1.0.255");
11+
12+
REQUIRE(parsed.has_value());
13+
CHECK(*parsed == ObisId(0, 0, 96, 1, 0, 255));
14+
}
15+
16+
SUBCASE("five-byte form defaults F to 255") {
17+
const auto parsed = ObisId::from_string("0-0.96.1.0");
18+
19+
REQUIRE(parsed.has_value());
20+
CHECK(*parsed == ObisId(0, 0, 96, 1, 0, 255));
21+
}
22+
23+
SUBCASE("rejects malformed strings") {
24+
CHECK_FALSE(ObisId::from_string("").has_value());
25+
CHECK_FALSE(ObisId::from_string(".0.96.1.0.255").has_value());
26+
CHECK_FALSE(ObisId::from_string("0..96.1.0.255").has_value());
27+
CHECK_FALSE(ObisId::from_string("0.0.96.1.0.").has_value());
28+
CHECK_FALSE(ObisId::from_string("0.0.96.1").has_value());
29+
CHECK_FALSE(ObisId::from_string("0.0.96.1.0.255.1").has_value());
30+
CHECK_FALSE(ObisId::from_string("0.0.256.1.0.255").has_value());
31+
CHECK_FALSE(ObisId::from_string("0.0.96.1.0x255").has_value());
32+
}
33+
}

0 commit comments

Comments
 (0)