Skip to content

Commit d2bea0c

Browse files
authored
Add support for C++ 20 ranges. (simdjson#1050)
C++ 20 adds a new feature called "ranges", which provides components for dealing with sequences of values: https://en.cppreference.com/w/cpp/ranges. A range is like a normal object containing `begin` and `end`, except there are also composable operations like maps, filters, joins, etc. The iterator objects returned by a range's `begin` and `end` require a more strict set of operations than is needed for a range-for loop. This PR adds the extra operations needed to support turning `dom::array` and `dom::object` into a range. This PR does not depend on any C++ 20 behavior, the added operators are all valid C++ 11, and are already part of the LegacyIterator concepts. This PR adds extra code behind: `#if defined(__cpp_lib_ranges)` guards, which is the new C++ 20 specified feature test macro for ranges support. When ranges support is detected, extra compile time checks are added to ensure that `dom::array` and `dom::object` satisfy the range concept. No runtime tests have been added yet because these compile time checks should be sufficient. If desired, the `static_assert` code could be moved out of the actual code headers and put into a test file.
1 parent f016c2b commit d2bea0c

7 files changed

Lines changed: 225 additions & 21 deletions

File tree

include/simdjson/dom/array.h

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,23 +23,42 @@ class array {
2323

2424
class iterator {
2525
public:
26+
using value_type = element;
27+
using difference_type = std::ptrdiff_t;
28+
2629
/**
2730
* Get the actual value
2831
*/
29-
inline element operator*() const noexcept;
32+
inline value_type operator*() const noexcept;
3033
/**
3134
* Get the next value.
3235
*
3336
* Part of the std::iterator interface.
3437
*
3538
*/
3639
inline iterator& operator++() noexcept;
40+
/**
41+
* Get the next value.
42+
*
43+
* Part of the std::iterator interface.
44+
*/
45+
inline iterator operator++(int) noexcept;
3746
/**
3847
* Check if these values come from the same place in the JSON.
3948
*
4049
* Part of the std::iterator interface.
4150
*/
4251
inline bool operator!=(const iterator& other) const noexcept;
52+
inline bool operator==(const iterator& other) const noexcept;
53+
54+
inline bool operator<(const iterator& other) const noexcept;
55+
inline bool operator<=(const iterator& other) const noexcept;
56+
inline bool operator>=(const iterator& other) const noexcept;
57+
inline bool operator>(const iterator& other) const noexcept;
58+
59+
iterator() noexcept = default;
60+
iterator(const iterator&) noexcept = default;
61+
iterator& operator=(const iterator&) noexcept = default;
4362
private:
4463
really_inline iterator(const internal::tape_ref &tape) noexcept;
4564
internal::tape_ref tape;
@@ -155,4 +174,16 @@ inline std::ostream& operator<<(std::ostream& out, const simdjson_result<dom::ar
155174

156175
} // namespace simdjson
157176

177+
#if defined(__cpp_lib_ranges)
178+
#include <ranges>
179+
180+
namespace std::ranges {
181+
template<>
182+
inline constexpr bool enable_view<simdjson::dom::array> = true;
183+
}
184+
185+
static_assert(std::ranges::view<simdjson::dom::array>);
186+
static_assert(std::ranges::sized_range<simdjson::dom::array>);
187+
#endif
188+
158189
#endif // SIMDJSON_DOM_ARRAY_H

include/simdjson/dom/object.h

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,13 @@ class object {
2424

2525
class iterator {
2626
public:
27+
using value_type = key_value_pair;
28+
using difference_type = std::ptrdiff_t;
29+
2730
/**
2831
* Get the actual key/value pair
2932
*/
30-
inline const key_value_pair operator*() const noexcept;
33+
inline const value_type operator*() const noexcept;
3134
/**
3235
* Get the next key/value pair.
3336
*
@@ -36,11 +39,24 @@ class object {
3639
*/
3740
inline iterator& operator++() noexcept;
3841
/**
39-
* Check if these key value pairs come from the same place in the JSON.
42+
* Get the next key/value pair.
43+
*
44+
* Part of the std::iterator interface.
45+
*
46+
*/
47+
inline iterator operator++(int) noexcept;
48+
/**
49+
* Check if these values come from the same place in the JSON.
4050
*
4151
* Part of the std::iterator interface.
4252
*/
4353
inline bool operator!=(const iterator& other) const noexcept;
54+
inline bool operator==(const iterator& other) const noexcept;
55+
56+
inline bool operator<(const iterator& other) const noexcept;
57+
inline bool operator<=(const iterator& other) const noexcept;
58+
inline bool operator>=(const iterator& other) const noexcept;
59+
inline bool operator>(const iterator& other) const noexcept;
4460
/**
4561
* Get the key of this key/value pair.
4662
*/
@@ -69,6 +85,10 @@ class object {
6985
* Get the value of this key/value pair.
7086
*/
7187
inline element value() const noexcept;
88+
89+
iterator() noexcept = default;
90+
iterator(const iterator&) noexcept = default;
91+
iterator& operator=(const iterator&) noexcept = default;
7292
private:
7393
really_inline iterator(const internal::tape_ref &tape) noexcept;
7494

@@ -261,4 +281,16 @@ inline std::ostream& operator<<(std::ostream& out, const simdjson_result<dom::ob
261281

262282
} // namespace simdjson
263283

284+
#if defined(__cpp_lib_ranges)
285+
#include <ranges>
286+
287+
namespace std::ranges {
288+
template<>
289+
inline constexpr bool enable_view<simdjson::dom::object> = true;
290+
}
291+
292+
static_assert(std::ranges::view<simdjson::dom::object>);
293+
static_assert(std::ranges::sized_range<simdjson::dom::object>);
294+
#endif
295+
264296
#endif // SIMDJSON_DOM_OBJECT_H

include/simdjson/inline/array.h

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -106,14 +106,33 @@ really_inline array::iterator::iterator(const internal::tape_ref &_tape) noexcep
106106
inline element array::iterator::operator*() const noexcept {
107107
return element(tape);
108108
}
109-
inline bool array::iterator::operator!=(const array::iterator& other) const noexcept {
110-
return tape.json_index != other.tape.json_index;
111-
}
112109
inline array::iterator& array::iterator::operator++() noexcept {
113110
tape.json_index = tape.after_element();
114111
return *this;
115112
}
116-
113+
inline array::iterator array::iterator::operator++(int) noexcept {
114+
array::iterator out = *this;
115+
++*this;
116+
return out;
117+
}
118+
inline bool array::iterator::operator!=(const array::iterator& other) const noexcept {
119+
return tape.json_index != other.tape.json_index;
120+
}
121+
inline bool array::iterator::operator==(const array::iterator& other) const noexcept {
122+
return tape.json_index == other.tape.json_index;
123+
}
124+
inline bool array::iterator::operator<(const array::iterator& other) const noexcept {
125+
return tape.json_index < other.tape.json_index;
126+
}
127+
inline bool array::iterator::operator<=(const array::iterator& other) const noexcept {
128+
return tape.json_index <= other.tape.json_index;
129+
}
130+
inline bool array::iterator::operator>=(const array::iterator& other) const noexcept {
131+
return tape.json_index >= other.tape.json_index;
132+
}
133+
inline bool array::iterator::operator>(const array::iterator& other) const noexcept {
134+
return tape.json_index > other.tape.json_index;
135+
}
117136
inline std::ostream& operator<<(std::ostream& out, const array &value) {
118137
return out << minify<array>(value);
119138
}

include/simdjson/inline/object.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,11 +149,31 @@ inline const key_value_pair object::iterator::operator*() const noexcept {
149149
inline bool object::iterator::operator!=(const object::iterator& other) const noexcept {
150150
return tape.json_index != other.tape.json_index;
151151
}
152+
inline bool object::iterator::operator==(const object::iterator& other) const noexcept {
153+
return tape.json_index == other.tape.json_index;
154+
}
155+
inline bool object::iterator::operator<(const object::iterator& other) const noexcept {
156+
return tape.json_index < other.tape.json_index;
157+
}
158+
inline bool object::iterator::operator<=(const object::iterator& other) const noexcept {
159+
return tape.json_index <= other.tape.json_index;
160+
}
161+
inline bool object::iterator::operator>=(const object::iterator& other) const noexcept {
162+
return tape.json_index >= other.tape.json_index;
163+
}
164+
inline bool object::iterator::operator>(const object::iterator& other) const noexcept {
165+
return tape.json_index > other.tape.json_index;
166+
}
152167
inline object::iterator& object::iterator::operator++() noexcept {
153168
tape.json_index++;
154169
tape.json_index = tape.after_element();
155170
return *this;
156171
}
172+
inline object::iterator object::iterator::operator++(int) noexcept {
173+
object::iterator out = *this;
174+
++*this;
175+
return out;
176+
}
157177
inline std::string_view object::iterator::key() const noexcept {
158178
return tape.get_string_view();
159179
}

singleheader/amalgamate_demo.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* auto-generated on Mon Jul 6 18:16:52 EDT 2020. Do not edit! */
1+
/* auto-generated on Sun 19 Jul 2020 11:55:45 PM EDT. Do not edit! */
22

33
#include <iostream>
44
#include "simdjson.h"

singleheader/simdjson.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* auto-generated on Mon Jul 6 18:16:52 EDT 2020. Do not edit! */
1+
/* auto-generated on Sun 19 Jul 2020 11:55:45 PM EDT. Do not edit! */
22
/* begin file src/simdjson.cpp */
33
#include "simdjson.h"
44

@@ -4610,7 +4610,7 @@ namespace logger {
46104610
namespace stage2 {
46114611
namespace atomparsing {
46124612

4613-
really_inline uint32_t string_to_uint32(const char* str) { return *reinterpret_cast<const uint32_t *>(str); }
4613+
really_inline uint32_t string_to_uint32(const char* str) { uint32_t val; std::memcpy(&val, str, sizeof(uint32_t)); return val; }
46144614

46154615
WARN_UNUSED
46164616
really_inline uint32_t str4ncmp(const uint8_t *src, const char* atom) {
@@ -6704,7 +6704,7 @@ namespace logger {
67046704
namespace stage2 {
67056705
namespace atomparsing {
67066706

6707-
really_inline uint32_t string_to_uint32(const char* str) { return *reinterpret_cast<const uint32_t *>(str); }
6707+
really_inline uint32_t string_to_uint32(const char* str) { uint32_t val; std::memcpy(&val, str, sizeof(uint32_t)); return val; }
67086708

67096709
WARN_UNUSED
67106710
really_inline uint32_t str4ncmp(const uint8_t *src, const char* atom) {
@@ -9926,7 +9926,7 @@ namespace logger {
99269926
namespace stage2 {
99279927
namespace atomparsing {
99289928

9929-
really_inline uint32_t string_to_uint32(const char* str) { return *reinterpret_cast<const uint32_t *>(str); }
9929+
really_inline uint32_t string_to_uint32(const char* str) { uint32_t val; std::memcpy(&val, str, sizeof(uint32_t)); return val; }
99309930

99319931
WARN_UNUSED
99329932
really_inline uint32_t str4ncmp(const uint8_t *src, const char* atom) {
@@ -13124,7 +13124,7 @@ namespace logger {
1312413124
namespace stage2 {
1312513125
namespace atomparsing {
1312613126

13127-
really_inline uint32_t string_to_uint32(const char* str) { return *reinterpret_cast<const uint32_t *>(str); }
13127+
really_inline uint32_t string_to_uint32(const char* str) { uint32_t val; std::memcpy(&val, str, sizeof(uint32_t)); return val; }
1312813128

1312913129
WARN_UNUSED
1313013130
really_inline uint32_t str4ncmp(const uint8_t *src, const char* atom) {

0 commit comments

Comments
 (0)