Skip to content

Commit fad142e

Browse files
committed
Merge branch 'Parser'
2 parents 6450a73 + 2a858b5 commit fad142e

6 files changed

Lines changed: 1157 additions & 0 deletions
Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
#include "../util/compact_binary_parser.hh"
2+
#include "doctest.h"
3+
4+
enum class Keys : uint16_t {
5+
BPM = 0x55,
6+
NotFound = 0x66,
7+
Track = 0xA0,
8+
Mode = 0xA2,
9+
};
10+
static_assert(sizeof(CompactBinaryParser<Keys>::Header) == 4);
11+
12+
static constexpr std::array<const uint8_t, 8> blob{0x55, 0x00, 0x04, 0x00, 120, 0x00, 0x00, 0x00};
13+
14+
static_assert(CompactBinaryParser<Keys>{blob}.get<uint32_t>(Keys::BPM).value_or(0) == 120);
15+
static_assert(CompactBinaryParser<Keys>{blob}.get<uint16_t>(Keys::BPM) == std::nullopt);
16+
static_assert(CompactBinaryParser<Keys>{blob}.get<uint32_t>(Keys::NotFound) == std::nullopt);
17+
18+
static constexpr std::array<const uint8_t, 14> blob2{
19+
0x55, 0x00, 0x04, 0x00, 120, 0x00, 0x00, 0x00, 0xA2, 0x00, 0x02, 0x00, 45, 0x00};
20+
21+
static_assert(CompactBinaryParser<Keys>{blob2}.get<uint32_t>(Keys::BPM).value_or(0) == 120);
22+
static_assert(CompactBinaryParser<Keys>{blob2}.get<uint16_t>(Keys::BPM) == std::nullopt);
23+
static_assert(CompactBinaryParser<Keys>{blob2}.get<uint32_t>(Keys::NotFound) == std::nullopt);
24+
static_assert(CompactBinaryParser<Keys>{blob2}.get<uint16_t>(Keys::Mode).value() == 45);
25+
26+
constexpr std::array<const uint8_t, 24> blob3{
27+
// clang-format off
28+
29+
// BPM:
30+
0x55, 0x00, 0x04, 0x00,
31+
120, 0x00, 0x00, 0x00,
32+
33+
// Track (12 bytes)
34+
0xA0, 0x00, 0x0C, 0x00,
35+
// Track::BPM = 180
36+
0x55, 0x00, 0x02, 0x00,
37+
180, 0x00,
38+
// Track::Mode = 4
39+
0xA2, 0x00, 0x02, 0x00,
40+
4, 0x00,
41+
42+
// clang-format on
43+
};
44+
45+
constexpr auto track = CompactBinaryParser<Keys>{blob3}.get<std::array<const uint8_t, 12>>(Keys::Track);
46+
static_assert(CompactBinaryParser<Keys>{*track}.get<uint16_t>(Keys::BPM) == 180);
47+
static_assert(CompactBinaryParser<Keys>{*track}.get<uint16_t>(Keys::Mode) == 4);
48+
49+
constexpr std::array<unsigned char, 5> blob_str = {'b', 'p', 'm', 0x1, 120};
50+
static_assert(CompactBinaryParser<const char[3], uint8_t>{blob_str}.get<uint8_t>("bpm") == 120);
51+
52+
TEST_CASE("Basic usage") {
53+
54+
enum class Keys : uint16_t {
55+
BPM = 0x55,
56+
Track = 0x56,
57+
NotFound = 0xAA,
58+
Mode = 0xAB,
59+
};
60+
61+
SUBCASE("Simply get a value") {
62+
constexpr std::array<const uint8_t, 8> blob{0x55, 0x00, 0x04, 0x00, 0x3F, 0x00, 0x00, 0x00};
63+
64+
static_assert(CompactBinaryParser<Keys>{blob}.get<uint32_t>(Keys::BPM).value_or(0) == 63);
65+
66+
auto bpm = CompactBinaryParser<Keys>{blob}.get<uint32_t>(Keys::BPM);
67+
CHECK(bpm.value() == 63);
68+
69+
auto mode = CompactBinaryParser<Keys>{blob}.get<uint32_t>(Keys::Mode);
70+
CHECK_FALSE(mode.has_value());
71+
72+
auto notfound = CompactBinaryParser<Keys>{blob}.get<uint16_t>(Keys::NotFound);
73+
CHECK_FALSE(notfound.has_value());
74+
}
75+
76+
SUBCASE("Check unaligned data") {
77+
constexpr uint8_t PAD = 0xFF; //add a byte to force the data to be un-aligned
78+
79+
alignas(4) std::array<const uint8_t, 15> blob{
80+
PAD, 0x55, 0x00, 0x04, 0x00, 120, 0x00, 0x00, 0x00, 0xAB, 0x00, 0x02, 0x00, 45, 0x00};
81+
// blob_misalign is guaranteed to not be aligned to a 2 or 4 byte boundary
82+
auto blob_misalign = std::span{blob}.subspan(1);
83+
84+
auto bpm = CompactBinaryParser<Keys>{blob_misalign}.get<uint32_t>(Keys::BPM);
85+
CHECK(bpm.value() == 120);
86+
87+
auto mode = CompactBinaryParser<Keys>{blob_misalign}.get<uint16_t>(Keys::Mode);
88+
CHECK(mode.value() == 45);
89+
90+
mode = CompactBinaryParser<Keys>{blob_misalign}.get<uint32_t>(Keys::Mode);
91+
CHECK_FALSE(mode.has_value());
92+
93+
auto notfound = CompactBinaryParser<Keys>{blob_misalign}.get<uint16_t>(Keys::NotFound);
94+
CHECK_FALSE(notfound.has_value());
95+
}
96+
97+
SUBCASE("Sub-objects") {
98+
std::array<const uint8_t, 24> blob{
99+
// clang-format off
100+
101+
// BPM:
102+
0x55, 0x00, 0x04, 0x00,
103+
120, 0x00, 0x00, 0x00,
104+
105+
// Track (12 bytes)
106+
0x56, 0x00, 0x0C, 0x00,
107+
// Track::BPM = 180
108+
0x55, 0x00, 0x02, 0x00,
109+
180, 0x00,
110+
// Track::Mode = 4
111+
0xAB, 0x00, 0x02, 0x00,
112+
4, 0x00,
113+
114+
// clang-format on
115+
};
116+
117+
SUBCASE("Copy sub-object, then parse") {
118+
auto track = CompactBinaryParser<Keys>{blob}.get<std::array<const uint8_t, 12>>(Keys::Track);
119+
CHECK(track.has_value());
120+
121+
auto track_bpm = CompactBinaryParser<Keys>{*track}.get<uint16_t>(Keys::BPM);
122+
CHECK(track_bpm.value() == 180);
123+
auto track_mode = CompactBinaryParser<Keys>{*track}.get<uint16_t>(Keys::Mode);
124+
CHECK(track_mode.value() == 4);
125+
}
126+
127+
SUBCASE("Parse sub-object in place in original data") {
128+
auto track = CompactBinaryParser<Keys>{blob}.get_node(Keys::Track);
129+
CHECK(track.size() == 12);
130+
CHECK(CompactBinaryParser<Keys>{blob}.get_size(Keys::Track) == 12);
131+
132+
auto track_bpm = CompactBinaryParser<Keys>{track}.get<uint16_t>(Keys::BPM);
133+
CHECK(track_bpm.value() == 180);
134+
auto track_mode = CompactBinaryParser<Keys>{track}.get<uint16_t>(Keys::Mode);
135+
CHECK(track_mode.value() == 4);
136+
137+
// non-existent sub-object
138+
auto no_exist = CompactBinaryParser<Keys>{blob}.get_node(Keys::Mode);
139+
CHECK(no_exist.size() == 0);
140+
}
141+
}
142+
143+
SUBCASE("Check bad data size") {
144+
145+
std::array<const uint8_t, 4> blob{0x55, 0x00, 0xFF, 0x00};
146+
147+
auto bpm = CompactBinaryParser<Keys>{blob}.get<uint32_t>(Keys::BPM);
148+
CHECK(!bpm.has_value());
149+
}
150+
151+
SUBCASE("Check header too small ") {
152+
153+
std::array<const uint8_t, 3> blob{0x55, 0x00, 0x04};
154+
155+
auto bpm = CompactBinaryParser<Keys>{blob}.get<uint32_t>(Keys::BPM);
156+
CHECK(!bpm.has_value());
157+
}
158+
159+
SUBCASE("Check missing data") {
160+
161+
std::array<const uint8_t, 5> blob{0x55, 0x00, 0x04, 0x00, 0x01};
162+
163+
auto bpm = CompactBinaryParser<Keys>{blob}.get<uint32_t>(Keys::BPM);
164+
CHECK(!bpm.has_value());
165+
}
166+
167+
SUBCASE("ASCII keys with char array") {
168+
std::array<unsigned char, 5> blob_str = {'b', 'p', 'm', 0x1, 120};
169+
auto p = CompactBinaryParser<const char[3], uint8_t>{blob_str};
170+
auto bpm = p.get<uint8_t>("bpm");
171+
CHECK(bpm.has_value());
172+
CHECK(bpm.value() == 120);
173+
}
174+
175+
SUBCASE("ASCII keys with char array and short key") {
176+
std::array<unsigned char, 15> blob_str = {
177+
'b', 'p', 'm', 0x1, 120, 't', '1', '\0', 0x1, 5, 'x', '\0', '\0', 0x1, 55};
178+
auto p = CompactBinaryParser<const char[3], uint8_t>{blob_str};
179+
180+
// "bpm" == 'b' 'p' 'm' '\0', but get() will only compare the first three bytes 'b' 'p' 'm'
181+
auto bpm = p.get<uint8_t>("bpm");
182+
CHECK(bpm.value() == 120);
183+
184+
// Don't need to pad the key since "t1\0" => 't' '1' '\0'
185+
auto t1 = p.get<uint8_t>("t1");
186+
CHECK(t1.value() == 5);
187+
188+
// Need to pad the key since 3 chars are required:"x\0" => 'x' '\0' '\0'
189+
auto x = p.get<uint8_t>("x");
190+
CHECK(x.value() == 55);
191+
}
192+
193+
SUBCASE("Zero-length data has no value even if key exists") {
194+
std::array<uint8_t, 2> blob{1, 0}; //key = 1, data-length = 0
195+
auto p = CompactBinaryParser<uint8_t, uint8_t>{blob};
196+
auto x = p.get<uint8_t>(1);
197+
CHECK(!x.has_value());
198+
}
199+
200+
SUBCASE("Empty blob") {
201+
std::array<uint8_t, 0> blob{};
202+
auto p = CompactBinaryParser<uint8_t, uint8_t>{blob};
203+
auto x = p.get<uint8_t>(0);
204+
CHECK(!x.has_value());
205+
}
206+
207+
SUBCASE("Multiple identical keys: return first found") {
208+
constexpr std::array<const uint8_t, 16> blob{
209+
// clang-format off
210+
0x55, 0x00, 0x04, 0x00, 50, 0x00, 0x00, 0x00,
211+
0x55, 0x00, 0x04, 0x00, 100, 0x00, 0x00, 0x00,
212+
// clang-format on
213+
};
214+
215+
auto bpm = CompactBinaryParser<Keys>{blob}.get<uint32_t>(Keys::BPM);
216+
CHECK(bpm.value() == 50);
217+
}
218+
}

0 commit comments

Comments
 (0)