RapidFuzz
Loading...
Searching...
No Matches
Range.hpp
1/* SPDX-License-Identifier: MIT */
2/* Copyright (c) 2022 Max Bachmann */
3
4#pragma once
5
6#include <algorithm>
7#include <utility>
8#include <cassert>
9#include <cstddef>
10#include <iterator>
11#include <limits>
12#include <ostream>
13#include <stdexcept>
14#include <stdint.h>
15#include <sys/types.h>
16#include <vector>
17
18#include <rapidfuzz/details/type_traits.hpp>
19
20namespace rapidfuzz {
21namespace detail {
22
23static inline void assume(bool b)
24{
25#if defined(__clang__)
26 __builtin_assume(b);
27#elif defined(__GNUC__) || defined(__GNUG__)
28 if (!b) __builtin_unreachable();
29#elif defined(_MSC_VER)
30 __assume(b);
31#endif
32}
33
34namespace to_begin_detail {
35using std::begin;
36
37template <typename CharT>
38CharT* to_begin(CharT* s)
39{
40 return s;
41}
42
43template <typename T>
44auto to_begin(T& x) -> decltype(begin(x))
45{
46
47 return begin(x);
48}
49} // namespace to_begin_detail
50
51using to_begin_detail::to_begin;
52
53namespace to_end_detail {
54using std::end;
55
56template <typename CharT>
57CharT* to_end(CharT* s)
58{
59 assume(s != nullptr);
60 while (*s != 0)
61 ++s;
62
63 return s;
64}
65
66template <typename T>
67auto to_end(T& x) -> decltype(end(x))
68{
69 return end(x);
70}
71} // namespace to_end_detail
72
73using to_end_detail::to_end;
74
75template <typename Iter>
76class Range {
77 Iter _first;
78 Iter _last;
79 // todo we might not want to cache the size for iterators
80 // that can can retrieve the size in O(1) time
81 size_t _size;
82
83public:
84 using value_type = typename std::iterator_traits<Iter>::value_type;
85 using iterator = Iter;
86 using reverse_iterator = std::reverse_iterator<iterator>;
87
88 Range(Iter first, Iter last) : _first(first), _last(last)
89 {
90 assert(std::distance(_first, _last) >= 0);
91 _size = static_cast<size_t>(std::distance(_first, _last));
92 }
93
94 Range(Iter first, Iter last, size_t size) : _first(first), _last(last), _size(size)
95 {}
96
97 template <typename T>
98 Range(T& x) : Range(to_begin(x), to_end(x))
99 {}
100
101 iterator begin() const noexcept
102 {
103 return _first;
104 }
105 iterator end() const noexcept
106 {
107 return _last;
108 }
109
110 reverse_iterator rbegin() const noexcept
111 {
112 return reverse_iterator(end());
113 }
114 reverse_iterator rend() const noexcept
115 {
116 return reverse_iterator(begin());
117 }
118
119 size_t size() const
120 {
121 return _size;
122 }
123
124 bool empty() const
125 {
126 return size() == 0;
127 }
128 explicit operator bool() const
129 {
130 return !empty();
131 }
132
133 template <typename... Dummy, typename IterCopy = Iter,
134 typename = rapidfuzz::rf_enable_if_t<
135 std::is_base_of<std::random_access_iterator_tag,
136 typename std::iterator_traits<IterCopy>::iterator_category>::value>>
137 auto operator[](size_t n) const -> decltype(*_first)
138 {
139 return _first[static_cast<ptrdiff_t>(n)];
140 }
141
142 void remove_prefix(size_t n)
143 {
144 std::advance(_first, static_cast<ptrdiff_t>(n));
145 _size -= n;
146 }
147
148 void remove_suffix(size_t n)
149 {
150 std::advance(_last, -static_cast<ptrdiff_t>(n));
151 _size -= n;
152 }
153
154 Range subseq(size_t pos = 0, size_t count = std::numeric_limits<size_t>::max())
155 {
156 if (pos > size()) throw std::out_of_range("Index out of range in Range::substr");
157
158 Range res = *this;
159 res.remove_prefix(pos);
160 if (count < res.size()) res.remove_suffix(res.size() - count);
161
162 return res;
163 }
164
165 const value_type& front() const
166 {
167 return *_first;
168 }
169
170 const value_type& back() const
171 {
172 return *(_last - 1);
173 }
174
175 Range<reverse_iterator> reversed() const
176 {
177 return {rbegin(), rend(), _size};
178 }
179
180 friend std::ostream& operator<<(std::ostream& os, const Range& seq)
181 {
182 os << "[";
183 for (auto x : seq)
184 os << static_cast<uint64_t>(x) << ", ";
185 os << "]";
186 return os;
187 }
188};
189
190template <typename Iter>
191auto make_range(Iter first, Iter last) -> Range<Iter>
192{
193 return Range<Iter>(first, last);
194}
195
196template <typename T>
197auto make_range(T& x) -> Range<decltype(to_begin(x))>
198{
199 return {to_begin(x), to_end(x)};
200}
201
202template <typename InputIt1, typename InputIt2>
203inline bool operator==(const Range<InputIt1>& a, const Range<InputIt2>& b)
204{
205 if (a.size() != b.size()) return false;
206
207 return std::equal(a.begin(), a.end(), b.begin());
208}
209
210template <typename InputIt1, typename InputIt2>
211inline bool operator!=(const Range<InputIt1>& a, const Range<InputIt2>& b)
212{
213 return !(a == b);
214}
215
216template <typename InputIt1, typename InputIt2>
217inline bool operator<(const Range<InputIt1>& a, const Range<InputIt2>& b)
218{
219 return (std::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end()));
220}
221
222template <typename InputIt1, typename InputIt2>
223inline bool operator>(const Range<InputIt1>& a, const Range<InputIt2>& b)
224{
225 return b < a;
226}
227
228template <typename InputIt1, typename InputIt2>
229inline bool operator<=(const Range<InputIt1>& a, const Range<InputIt2>& b)
230{
231 return !(b < a);
232}
233
234template <typename InputIt1, typename InputIt2>
235inline bool operator>=(const Range<InputIt1>& a, const Range<InputIt2>& b)
236{
237 return !(a < b);
238}
239
240template <typename InputIt>
241using RangeVec = std::vector<Range<InputIt>>;
242
243} // namespace detail
244} // namespace rapidfuzz