Skip to content

Commit d509620

Browse files
committed
Add limited span support
1 parent 5220157 commit d509620

9 files changed

Lines changed: 350 additions & 39 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@
22

33
## v0.5.0
44

5-
* Rename event_consumer methods from (begin|end)\_(list|dict) to (begin|end)\_(list|dict).
6-
This makes naming more consistent.
7-
* Add more benchmarks
5+
* More consisten naming by changing event_consumer methods from (begin|end)\_(list|dict)
6+
to (begin|end)\_(list|dict).
7+
* Add more benchmarks.
88
* Disable contract checks in release build for improved performance.
99
* Fix parsing from a pair of InputItererators.
1010
* Add string_parsing_mode options to push_parser to allow parsing strings to string_view.
1111
* Add experimental SSE4.1 and AVX2 integer parsing backends. Enable SWAR integer parsing by default.
12+
* Add limited std::span serialization/deserialization support.
1213

1314
## v0.4.0
1415

README.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
[**Integration**](#Integration) |
1919
[**License**](#License)
2020

21-
A header-only C++20 bencode serialization/deserialization library. Inspired by toa
21+
A header-only C++20 bencode serialization/deserialization library.
2222

2323
## Features
2424

@@ -38,9 +38,11 @@ A header-only C++20 bencode serialization/deserialization library. Inspired by t
3838
stack-based buffer overflow attacks. Integer parsing throws when overflows are encountered.
3939
* **Speed**. While not the primary goal of this project this library provides optimized integer parsing with
4040
SWAR techniques. Benchmarks show this library performs well in comparison with other libraries
41-
42-
## Status
41+
* **Well-tested**. This project achieves 95% testing coverage.
42+
We also run Sanitizers in a CI pipeline to check for leaks and undefined-behavior.
4343

44+
## Status
45+
4446
This library is under active development. The API may change at any release prior to 1.0.0.
4547
Versioning follows the Semantic Versioning Specification.
4648

external/arvidn-libtorrent.cmake

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,5 @@ else()
1111
GIT_TAG RC_2_0
1212
)
1313
FetchContent_MakeAvailable(libtorrent-rasterbar)
14-
endif()
14+
endif()
15+

include/bencode/detail/bvalue/conversion.hpp

Lines changed: 60 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#pragma once
33

44
#include <nonstd/expected.hpp>
5+
#include <span>
56

67
#include "bencode/detail/symbol.hpp"
78
#include "bencode/detail/utils.hpp"
@@ -40,35 +41,54 @@ convert_from_bvalue_string_impl(customization_point_type<const char*>,
4041
return s.c_str();
4142
}
4243

43-
// Conversion via iterators pair constructor
44+
// Conversion to T via pair of iterators
4445

45-
template <typename T, typename Policy>
46+
template <typename T, basic_bvalue_instantiation U, typename Policy = typename std::remove_cvref_t<U>::policy_type>
4647
requires
47-
std::constructible_from<
48-
typename policy_string_t<Policy>::value_type, rng::range_value_t<T>> &&
49-
std::constructible_from<
50-
policy_string_t<Policy>, rng::iterator_t<T>, rng::sentinel_t<T>>
48+
std::constructible_from<typename policy_string_t<Policy>::value_type,
49+
rng::range_value_t<T>> &&
50+
std::constructible_from<T, rng::iterator_t<policy_string_t<Policy>>,
51+
rng::sentinel_t<policy_string_t<Policy>>>
5152
constexpr nonstd::expected<T, conversion_errc>
5253
convert_from_bvalue_string_impl(customization_point_type<T>,
53-
const basic_bvalue<Policy>& b,
54-
priority_tag<1>) noexcept
54+
U&& b,
55+
priority_tag<2>) noexcept
5556
{
5657
if (!holds_string(b)) [[unlikely]]
5758
return nonstd::make_unexpected(conversion_errc::not_string_type);
5859

59-
const auto& bstring = get_string(b);
60+
auto& bstring = get_string(b);
6061
try { return T(rng::begin(bstring), rng::end(bstring)); }
6162
catch (...) { return nonstd::make_unexpected(conversion_errc::construction_error); }
6263
}
6364

65+
// Conversion to T via pointer and size
66+
67+
template <typename T, basic_bvalue_instantiation U, typename Policy = typename std::remove_cvref_t<U>::policy_type>
68+
requires rng::contiguous_range<policy_string_t<Policy>> &&
69+
std::constructible_from<T, rng::range_value_t<policy_string_t<Policy>>*,
70+
rng::range_size_t<policy_string_t<Policy>>>
71+
constexpr nonstd::expected<T, conversion_errc>
72+
convert_from_bvalue_string_impl(customization_point_type<T>,
73+
U&& b,
74+
priority_tag<3>) noexcept
75+
{
76+
if (!holds_string(b)) [[unlikely]]
77+
return nonstd::make_unexpected(conversion_errc::not_string_type);
78+
79+
auto& bstring = get_string(b);
80+
try { return T(rng::data(bstring), rng::size(bstring)); }
81+
catch (...) { return nonstd::make_unexpected(conversion_errc::construction_error); }
82+
}
83+
6484
// Conversion through std::string_view.
6585

66-
template <typename T, typename Policy>
86+
template <typename T, basic_bvalue_instantiation U, typename Policy = typename std::remove_cvref_t<U>::policy_type>
6787
requires std::constructible_from<T, std::string_view> &&
6888
std::convertible_to<policy_string_t<Policy>, std::string_view>
6989
constexpr nonstd::expected<T, conversion_errc>
7090
convert_from_bvalue_string_impl(customization_point_type<T>,
71-
const basic_bvalue<Policy>& b,
91+
U&& b,
7292
priority_tag<0>) noexcept
7393

7494
{
@@ -79,23 +99,23 @@ convert_from_bvalue_string_impl(customization_point_type<T>,
7999
}
80100

81101

82-
// Conversion of byte stringsauto
102+
// Conversion of byte strings auto
83103

84-
template <typename T, typename Policy>
85-
requires std::same_as<rng::range_value_t<T>, std::byte> &&
86-
std::constructible_from<T, const std::byte*, const std::byte*> &&
104+
template <typename T, basic_bvalue_instantiation U, typename Policy = typename std::remove_cvref_t<U>::policy_type>
105+
requires std::same_as<std::remove_cv_t<rng::range_value_t<T>>, std::byte> &&
106+
std::constructible_from<T, rng::range_value_t<T>*, rng::range_value_t<T>*> &&
87107
rng::contiguous_range<policy_string_t<Policy>>
88108
constexpr nonstd::expected<T, conversion_errc> convert_from_bvalue_string_impl(
89109
customization_point_type<T>,
90-
const basic_bvalue<Policy>& b,
110+
U&& b,
91111
priority_tag<0>) noexcept
92112
{
93113
if (!holds_string(b)) [[unlikely]]
94114
return nonstd::make_unexpected(conversion_errc::not_string_type);
95115

96-
const auto& bstring = get_string(b);
97-
try { return T(reinterpret_cast<const std::byte*>(rng::data(bstring)),
98-
reinterpret_cast<const std::byte*>(rng::data(bstring)+rng::size(bstring))); }
116+
auto& bstring = get_string(b);
117+
try { return T(reinterpret_cast<std::byte*>(rng::data(bstring)),
118+
reinterpret_cast<std::byte*>(rng::data(bstring)+rng::size(bstring))); }
99119
catch (...) { return nonstd::make_unexpected(conversion_errc::construction_error); }
100120
}
101121

@@ -167,7 +187,6 @@ constexpr nonstd::expected<T, conversion_errc> convert_from_bvalue_list_impl(
167187
std::apply(
168188
[&]<std::size_t... IS>(std::index_sequence<IS...>&&) constexpr {
169189
using std::get;
170-
171190
( (get<IS>(out) = get_as<std::tuple_element_t<IS, T>>(
172191
detail::forward_like<BV>(blist[IS]))) , ... );
173192
},
@@ -181,6 +200,27 @@ constexpr nonstd::expected<T, conversion_errc> convert_from_bvalue_list_impl(
181200
return out;
182201
}
183202

203+
// conversion to std::span<const char> or std::span<const std::byte>
204+
205+
template <typename Tp, basic_bvalue_instantiation BV, typename U = std::remove_cvref_t<BV>>
206+
requires (std::same_as<Tp, const char> || std::same_as<Tp, const std::byte>)
207+
&& std::same_as<typename detail::policy_string_t<BV>::value_type, char>
208+
constexpr nonstd::expected<std::span<const Tp>, conversion_errc> convert_from_bvalue_list_impl(
209+
customization_point_type<std::span<const Tp>>,
210+
BV&& b,
211+
priority_tag<0>) noexcept
212+
{
213+
if (!holds_string(b)) [[unlikely]]
214+
return nonstd::make_unexpected(conversion_errc::not_list_type);
215+
216+
const auto& s = get_string(b);
217+
if constexpr (std::same_as<Tp, std::byte>) {
218+
return std::span(reinterpret_cast<const std::byte*>(s.data()), s.size());
219+
} else {
220+
return std::span(s.data(), s.size());
221+
}
222+
}
223+
184224
// Dict conversion
185225

186226
template <typename T, basic_bvalue_instantiation BV, typename Policy = typename std::remove_cvref_t<BV>::policy_type>

include/bencode/detail/bview/comparison.hpp

Lines changed: 52 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -31,31 +31,52 @@ constexpr auto compare_equality_with_bview_integer_impl(
3131
}
3232

3333
template <typename T>
34-
requires std::equality_comparable_with<std::string_view, T>
34+
requires std::equality_comparable_with<T, std::string_view>
3535
constexpr bool compare_equality_with_bview_string_impl(
3636
customization_point_type<T>,
3737
const bview& bview,
3838
const T& value,
3939
priority_tag<1>)
4040
{
4141
if (!holds_string(bview)) return false;
42-
return (get_string(bview) == value);
42+
return (std::string_view(get_string(bview)) == value);
4343
}
4444

4545
template <typename T>
46-
constexpr bool compare_equality_with_bvalue_string_impl(
46+
requires std::equality_comparable_with<rng::range_value_t<T>, string_bview::value_type>
47+
constexpr bool compare_equality_with_bview_string_impl(
4748
customization_point_type<T>,
48-
const bview& bv,
49+
const bview& bview,
4950
const T& value,
5051
priority_tag<0>)
5152
{
52-
if (!holds_string(bv)) return false;
53-
const auto& s = get_string(bv);
54-
return std::lexicographical_compare(
53+
if (!holds_string(bview)) return false;
54+
const auto s = get_string(bview);
55+
return std::equal(
5556
rng::begin(s), rng::end(s),
5657
rng::begin(value), rng::end(value));
5758
}
5859

60+
61+
/// Comparison for byte strings
62+
template <typename T>
63+
requires std::same_as<std::remove_cv_t<rng::range_value_t<T>>, std::byte>
64+
constexpr bool compare_equality_with_bview_string_impl(
65+
customization_point_type<T>,
66+
const bview& bview,
67+
const T& value,
68+
priority_tag<0>)
69+
{
70+
if (!holds_string(bview)) return false;
71+
const auto& s = get_string(bview);
72+
return std::equal(
73+
rng::begin(s), rng::end(s),
74+
rng::begin(value), rng::end(value),
75+
[] (char lhs, std::byte rhs) {
76+
return short(lhs) == to_integer<short>(rhs);
77+
});
78+
}
79+
5980
template <typename T>
6081
constexpr bool compare_equality_with_bview_list_impl(
6182
customization_point_type<T>,
@@ -116,12 +137,12 @@ constexpr std::weak_ordering compare_three_way_with_bview_string_impl(
116137
priority_tag<1>)
117138
{
118139
if (!holds_string(bview)) return (bview.type() <=> bencode_type::string);
119-
return (get_string(bview) <=> value);
140+
return (std::string_view(get_string(bview)) <=> value);
120141
}
121142

122143

123144
template <typename T>
124-
requires std::three_way_comparable<rng::range_value_t<T>, string_bview::value_type>
145+
requires std::three_way_comparable_with<rng::range_value_t<T>, string_bview::value_type>
125146
constexpr std::weak_ordering compare_three_way_with_bview_string_impl(
126147
customization_point_type<T>,
127148
const bview& b,
@@ -136,6 +157,25 @@ constexpr std::weak_ordering compare_three_way_with_bview_string_impl(
136157
rng::begin(value), rng::end(value));
137158
}
138159

160+
template <typename T>
161+
requires std::same_as<std::remove_cv_t<rng::range_value_t<T>>, std::byte>
162+
constexpr std::weak_ordering compare_three_way_with_bview_string_impl(
163+
customization_point_type<T>,
164+
const bview& b,
165+
const T& value,
166+
priority_tag<0>)
167+
{
168+
if (!holds_string(b)) return (b.type() <=> bencode_type::string);
169+
const auto& bstring = get_string(b);
170+
171+
return std::lexicographical_compare_three_way(
172+
rng::begin(bstring), rng::end(bstring),
173+
rng::begin(value), rng::end(value),
174+
[] (char lhs, std::byte rhs) {
175+
return short(lhs) <=> to_integer<short>(rhs);
176+
});
177+
}
178+
139179

140180

141181
template <typename T>
@@ -219,15 +259,15 @@ constexpr bool compare_equality_with_bview(const bview& bv, const T& value)
219259
}
220260
else if constexpr (bencode::serialization_traits<T>::type == bencode_type::string) {
221261
return compare_equality_with_bview_string_impl(
222-
customization_for<T>, bv,value, priority_tag<1>{});
262+
customization_for<T>, bv, value, priority_tag<2>{});
223263
}
224264
else if constexpr (bencode::serialization_traits<T>::type == bencode_type::list) {
225265
return compare_equality_with_bview_list_impl(
226-
customization_for<T>, bv,value, priority_tag<0>{});
266+
customization_for<T>, bv, value, priority_tag<0>{});
227267
}
228268
else if constexpr (bencode::serialization_traits<T>::type == bencode_type::dict) {
229269
return compare_equality_with_bview_dict_impl(
230-
customization_for<T>, bv,value, priority_tag<0>{});
270+
customization_for<T>, bv, value, priority_tag<0>{});
231271
}
232272
else {
233273
static_assert(detail::always_false<T>::value, "no serializer for T found, check includes!");

include/bencode/traits/all.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "string.hpp"
1414
#include "string_view.hpp"
1515
#include "stringstream.hpp"
16+
#include "span.hpp"
1617
#include "tuple.hpp"
1718
#include "unordered_map.hpp"
1819
#include "unordered_set.hpp"

include/bencode/traits/span.hpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#pragma once
2+
3+
#include <span>
4+
#include "bencode/detail/serialization_traits.hpp"
5+
6+
namespace bencode {
7+
8+
template <std::size_t Extent>
9+
struct serialization_traits<std::span<char, Extent>> : serializes_to_string {};
10+
11+
template <std::size_t Extent>
12+
struct serialization_traits<std::span<const char, Extent>> : serializes_to_string {};
13+
14+
template <std::size_t Extent>
15+
struct serialization_traits<std::span<std::byte, Extent>> : serializes_to_string {};
16+
17+
template <std::size_t Extent>
18+
struct serialization_traits<std::span<const std::byte, Extent>> : serializes_to_string {};
19+
20+
template <serializable T, std::size_t Extent>
21+
struct serialization_traits<std::span<T, Extent>> : serializes_to_list {};
22+
23+
template <serializable T, std::size_t Extent>
24+
struct serialization_traits<std::span<const T, Extent>> : serializes_to_list {};
25+
26+
}

tests/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,9 @@ target_sources(bencode-tests
3636
test_bencode_type.cpp
3737
test_connect.cpp
3838
test_bpointer.cpp
39+
3940
traits/memory.cpp
41+
traits/span.cpp
4042
)
4143

4244
target_link_libraries(bencode-tests PRIVATE

0 commit comments

Comments
 (0)