Skip to content

Commit 3201e9f

Browse files
committed
Add exceptions for error handling
1 parent cff2ae6 commit 3201e9f

File tree

3 files changed

+191
-15
lines changed

3 files changed

+191
-15
lines changed

include/msgpack23/msgpack23.h

+28-15
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ namespace msgpack23 {
173173
requires MapLike<T> && (!EnumLike<T>)
174174
void pack_type(T const &value) {
175175
if (!pack_map_header(value.size())) {
176-
return;
176+
throw std::length_error("Map is too long to be serialized.");
177177
}
178178
for (auto const &item: value) {
179179
pack_type(std::get<0>(item));
@@ -185,7 +185,7 @@ namespace msgpack23 {
185185
requires (!MapLike<T> && !EnumLike<T>)
186186
void pack_type(T const &value) {
187187
if (!pack_array_header(value.size())) {
188-
return;
188+
throw std::length_error("Collection is too long to be serialized.");
189189
}
190190
for (auto const &item: value) {
191191
pack_type(item);
@@ -378,7 +378,7 @@ namespace msgpack23 {
378378
emplace_constant(FormatConstants::str32);
379379
emplace_integral(static_cast<std::uint32_t>(value.size()));
380380
} else {
381-
return; // Give up if string is too long
381+
throw std::length_error("String is too long to be serialized.");
382382
}
383383

384384
data_.reserve(data_.size() + value.size());
@@ -401,7 +401,7 @@ namespace msgpack23 {
401401
emplace_constant(FormatConstants::bin32);
402402
emplace_integral(static_cast<std::uint32_t>(value.size()));
403403
} else {
404-
return; // Give up if vector is too large
404+
throw std::length_error("Vector is too long to be serialized.");
405405
}
406406

407407
data_.reserve(data_.size() + value.size());
@@ -439,13 +439,14 @@ namespace msgpack23 {
439439
if (position_ < data_.size()) {
440440
return data_[position_];
441441
}
442-
return static_cast<std::byte>(0);
442+
throw std::out_of_range("Unpacker doesn't have enough data.");
443443
}
444444

445445
void increment(std::size_t const count = 1) {
446-
if (position_ + count < data_.size()) {
447-
position_ += count;
446+
if (position_ + count > data_.size()) {
447+
throw std::out_of_range("Unpacker doesn't have enough data.");
448448
}
449+
position_ += count;
449450
}
450451

451452
[[nodiscard]] bool check_constant(FormatConstants const &value) const {
@@ -454,9 +455,12 @@ namespace msgpack23 {
454455

455456
template<typename T, std::enable_if_t<std::is_unsigned_v<T>, int> = 0>
456457
[[nodiscard]] T read_integral() {
458+
if (position_ + sizeof(T) > data_.size()) {
459+
throw std::out_of_range("Unpacker doesn't have enough data.");
460+
}
457461
T result{};
458462
std::memcpy(&result, data_.data() + position_, sizeof(T));
459-
position_ += sizeof(T);
463+
increment(sizeof(T));
460464
result = from_big_endian(result);
461465
return result;
462466
}
@@ -580,6 +584,7 @@ namespace msgpack23 {
580584
return;
581585
}
582586
}
587+
throw std::logic_error("Unexpected value");
583588
}
584589

585590
template<typename... Elements>
@@ -748,6 +753,8 @@ namespace msgpack23 {
748753
increment();
749754
auto const data = read_integral<std::uint32_t>();
750755
value = std::bit_cast<float>(data);
756+
} else {
757+
throw std::logic_error("Unexpected value");
751758
}
752759
}
753760

@@ -757,6 +764,8 @@ namespace msgpack23 {
757764
increment();
758765
auto const data = read_integral<std::uint64_t>();
759766
value = std::bit_cast<double>(data);
767+
} else {
768+
throw std::logic_error("Unexpected value");
760769
}
761770
}
762771

@@ -770,10 +779,11 @@ namespace msgpack23 {
770779
str_size = std::to_integer<std::size_t>(current() & static_cast<std::byte>(0b00011111));
771780
increment();
772781
}
773-
if (position_ + str_size <= data_.size()) {
774-
value = std::string(reinterpret_cast<const char *>(data_.data() + position_), str_size);
775-
increment(str_size);
782+
if (position_ + str_size > data_.size()) {
783+
throw std::out_of_range("String position is out of range");
776784
}
785+
value = std::string(reinterpret_cast<const char *>(data_.data() + position_), str_size);
786+
increment(str_size);
777787
}
778788

779789
template<>
@@ -782,12 +792,15 @@ namespace msgpack23 {
782792
if (read_conditional<FormatConstants::bin32, std::uint32_t>(bin_size)) {
783793
} else if (read_conditional<FormatConstants::bin16, std::uint16_t>(bin_size)) {
784794
} else if (read_conditional<FormatConstants::bin8, std::uint8_t>(bin_size)) {
795+
} else {
796+
throw std::logic_error("Unexpected value");
785797
}
786-
if (position_ + bin_size <= data_.size()) {
787-
auto const *src = reinterpret_cast<std::uint8_t const *>(data_.data() + position_);
788-
value.assign(src, src + bin_size);
789-
increment(bin_size);
798+
if (position_ + bin_size > data_.size()) {
799+
throw std::out_of_range("Vector position is out of range");
790800
}
801+
auto const *src = reinterpret_cast<std::uint8_t const *>(data_.data() + position_);
802+
value.assign(src, src + bin_size);
803+
increment(bin_size);
791804
}
792805

793806
template<typename T>

tests/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ add_executable(
88
msgpack23_tests
99
array_tests.cpp
1010
chrono_tests.cpp
11+
exception_tests.cpp
1112
int8_tests.cpp
1213
int16_tests.cpp
1314
int32_tests.cpp

tests/exception_tests.cpp

+162
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
//
2+
// Created by Rene Windegger on 22/02/2025.
3+
//
4+
5+
#include <list>
6+
#include <gtest/gtest.h>
7+
#include <msgpack23/msgpack23.h>
8+
9+
namespace {
10+
TEST(msgpack23, MapTooLargeTest) {
11+
GTEST_SKIP() << "Required map is to large for memory";
12+
msgpack23::Packer packer{};
13+
std::map<std::size_t, std::size_t> expected{};
14+
static_assert(
15+
std::numeric_limits<std::uint32_t>::max() < std::numeric_limits<std::map<std::size_t,
16+
std::size_t>::size_type>::max(), "Maps cannot be larger than uint32_t.");
17+
for (std::map<std::size_t, std::size_t>::size_type i = 0;
18+
i < static_cast<std::map<std::size_t, std::size_t>::size_type>(std::numeric_limits<std::uint32_t>::max()) +
19+
1; ++i) {
20+
expected.insert(std::make_pair(i, i));
21+
}
22+
EXPECT_THROW(auto _ = packer(expected), std::length_error);
23+
}
24+
25+
TEST(msgpack23, CollectionTooLargeTest) {
26+
msgpack23::Packer packer{};
27+
std::vector<std::size_t> expected{};
28+
static_assert(
29+
std::numeric_limits<std::uint32_t>::max() < std::numeric_limits<std::vector<std::size_t>::size_type>::max(),
30+
"Vectors cannot be larger than uint32_t.");
31+
expected.resize(
32+
static_cast<std::vector<std::size_t>::size_type>(std::numeric_limits<std::uint32_t>::max()) + 1);
33+
EXPECT_THROW(auto _ = packer(expected), std::length_error);
34+
}
35+
36+
TEST(msgpack23, StringTooLargeTest) {
37+
msgpack23::Packer packer{};
38+
std::string expected{};
39+
static_assert(std::numeric_limits<std::uint32_t>::max() < std::numeric_limits<std::string::size_type>::max(),
40+
"Strings cannot be larger than uint32_t.");
41+
expected.resize(static_cast<std::string::size_type>(std::numeric_limits<std::uint32_t>::max()) + 1);
42+
EXPECT_THROW(auto _ = packer(expected), std::length_error);
43+
}
44+
45+
TEST(msgpack23, VectorTooLargeTest) {
46+
msgpack23::Packer packer{};
47+
std::vector<std::uint8_t> expected{};
48+
static_assert(
49+
std::numeric_limits<std::uint32_t>::max() < std::numeric_limits<std::vector<
50+
std::uint8_t>::size_type>::max(), "Vectors cannot be larger than uint32_t.");
51+
expected.resize(
52+
static_cast<std::vector<std::uint8_t>::size_type>(std::numeric_limits<std::uint32_t>::max()) + 1);
53+
EXPECT_THROW(auto _ = packer(expected), std::length_error);
54+
}
55+
56+
TEST(msgpack23, WrongFormatForFloatTest) {
57+
msgpack23::Packer packer{};
58+
constexpr double expected{3.1415};
59+
auto const data = packer(expected);
60+
float actual{};
61+
msgpack23::Unpacker unpacker{data};
62+
EXPECT_THROW(unpacker(actual), std::logic_error);
63+
}
64+
65+
TEST(msgpack23, WrongFormatForDoubleTest) {
66+
msgpack23::Packer packer{};
67+
constexpr float expected{3.1415F};
68+
auto const data = packer(expected);
69+
double actual{};
70+
msgpack23::Unpacker unpacker{data};
71+
EXPECT_THROW(unpacker(actual), std::logic_error);
72+
}
73+
74+
TEST(msgpack23, WrongFormatForTimeStampTest) {
75+
msgpack23::Packer packer{};
76+
constexpr std::uint64_t expected{std::numeric_limits<std::uint64_t>::max()};
77+
auto const data = packer(expected);
78+
std::chrono::system_clock::time_point actual{};
79+
msgpack23::Unpacker unpacker{data};
80+
EXPECT_THROW(unpacker(actual), std::logic_error);
81+
}
82+
83+
TEST(msgpack23, WrongFormatForByteArrayTest) {
84+
msgpack23::Packer packer{};
85+
constexpr std::uint64_t expected{std::numeric_limits<std::uint64_t>::max()};
86+
auto const data = packer(expected);
87+
std::vector<std::uint8_t> actual{};
88+
msgpack23::Unpacker unpacker{data};
89+
EXPECT_THROW(unpacker(actual), std::logic_error);
90+
}
91+
92+
TEST(msgpack23, StringNotEnoughDataTest) {
93+
std::vector<std::byte> const expected_data{
94+
static_cast<std::byte>(0b10100000 | 4),
95+
static_cast<std::byte>('t'),
96+
static_cast<std::byte>('e')
97+
};
98+
msgpack23::Unpacker unpacker{expected_data};
99+
std::string actual{};
100+
EXPECT_THROW(unpacker(actual), std::out_of_range);
101+
}
102+
103+
TEST(msgpack23, ByteArrayNotEnoughDataTest) {
104+
std::vector<std::byte> const expected_data{
105+
static_cast<std::byte>(0xc4),
106+
static_cast<std::byte>(4),
107+
static_cast<std::byte>(1),
108+
static_cast<std::byte>(2),
109+
};
110+
msgpack23::Unpacker unpacker{expected_data};
111+
std::vector<std::uint8_t> actual{};
112+
EXPECT_THROW(unpacker(actual), std::out_of_range);
113+
}
114+
115+
TEST(msgpack23, ArrayNotEnoughDataTest) {
116+
std::vector<std::byte> const expected_data{
117+
static_cast<std::byte>(0b10010000 | 3),
118+
static_cast<std::byte>(0b10100000 | 3),
119+
static_cast<std::byte>('o'),
120+
static_cast<std::byte>('n'),
121+
static_cast<std::byte>('e'),
122+
static_cast<std::byte>(0b10100000 | 3),
123+
static_cast<std::byte>('t'),
124+
static_cast<std::byte>('w'),
125+
static_cast<std::byte>('o')
126+
};
127+
msgpack23::Unpacker unpacker{expected_data};
128+
std::list<std::string> actual{};
129+
EXPECT_THROW(unpacker(actual), std::out_of_range);
130+
}
131+
132+
TEST(msgpack23, MapNotEnoughDataTest) {
133+
std::vector<std::byte> const expected_data{
134+
static_cast<std::byte>(0b10000000 | 2),
135+
static_cast<std::byte>(0),
136+
static_cast<std::byte>(0b10100000 | 4),
137+
static_cast<std::byte>('z'),
138+
static_cast<std::byte>('e'),
139+
static_cast<std::byte>('r'),
140+
static_cast<std::byte>('o'),
141+
static_cast<std::byte>(1),
142+
};
143+
msgpack23::Unpacker unpacker{expected_data};
144+
std::map<std::uint8_t, std::string> actual{};
145+
EXPECT_THROW(unpacker(actual), std::out_of_range);
146+
}
147+
148+
TEST(msgpack23, IntegralNotEnoughDataTest) {
149+
std::vector<std::byte> const expected_data{
150+
static_cast<std::byte>(0xcf),
151+
static_cast<std::byte>(0),
152+
static_cast<std::byte>('z'),
153+
static_cast<std::byte>('e'),
154+
static_cast<std::byte>('r'),
155+
static_cast<std::byte>('o'),
156+
static_cast<std::byte>(1),
157+
};
158+
msgpack23::Unpacker unpacker{expected_data};
159+
std::uint64_t actual{};
160+
EXPECT_THROW(unpacker(actual), std::out_of_range);
161+
}
162+
}

0 commit comments

Comments
 (0)