Skip to content

Commit 9a2a63b

Browse files
authored
Homogeneous default allocators (#552)
Homogeneous default allocators
1 parent b217c2b commit 9a2a63b

File tree

4 files changed

+86
-34
lines changed

4 files changed

+86
-34
lines changed

include/sparrow/buffer/buffer.hpp

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -71,10 +71,10 @@ namespace sparrow
7171
constexpr buffer_base(const A& a) noexcept;
7272

7373
template <allocator A = allocator_type>
74-
constexpr buffer_base(size_type n, const A& a = A(xsimd::aligned_allocator<T>()));
74+
constexpr buffer_base(size_type n, const A& a);
7575

7676
template <allocator A = allocator_type>
77-
constexpr buffer_base(pointer p, size_type n, const A& a = A(xsimd::aligned_allocator<T>()));
77+
constexpr buffer_base(pointer p, size_type n, const A& a);
7878

7979
~buffer_base();
8080

@@ -122,6 +122,7 @@ namespace sparrow
122122
public:
123123

124124
using allocator_type = typename base_type::allocator_type;
125+
using default_allocator = std::allocator<T>;
125126
using value_type = T;
126127
using reference = value_type&;
127128
using const_reference = const value_type&;
@@ -143,22 +144,22 @@ namespace sparrow
143144
{
144145
}
145146

146-
template <allocator A = allocator_type>
147+
template <allocator A = default_allocator>
147148
constexpr explicit buffer(size_type n, const A& a = A());
148149

149-
template <allocator A = allocator_type>
150+
template <allocator A = default_allocator>
150151
constexpr buffer(size_type n, const value_type& v, const A& a = A());
151152

152-
template <allocator A = allocator_type>
153+
template <allocator A = default_allocator>
153154
constexpr buffer(pointer p, size_type n, const A& a = A());
154155

155-
template <allocator A = allocator_type>
156+
template <allocator A = default_allocator>
156157
constexpr buffer(std::initializer_list<value_type> init, const A& a = A());
157158

158-
template <class It, allocator A = allocator_type>
159+
template <class It, allocator A = default_allocator>
159160
constexpr buffer(It first, It last, const A& a = A());
160161

161-
template <std::ranges::input_range Range, allocator A = allocator_type>
162+
template <std::ranges::input_range Range, allocator A = default_allocator>
162163
requires(std::same_as<std::ranges::range_value_t<Range>, T> && !is_buffer_view<Range>)
163164
constexpr buffer(const Range& range, const A& a = A());
164165

include/sparrow/buffer/dynamic_bitset/dynamic_bitset.hpp

Lines changed: 56 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,10 @@ namespace sparrow
5959

6060
using base_type = dynamic_bitset_base<buffer<T>>; ///< Base class type
6161
using storage_type = typename base_type::storage_type; ///< Underlying storage container type
62-
using block_type = typename base_type::block_type; ///< Type of each storage block (same as T)
63-
using value_type = typename base_type::value_type; ///< Type of individual bit values (bool)
64-
using size_type = typename base_type::size_type; ///< Type used for sizes and indices
62+
using default_allocator = typename storage_type::default_allocator; ///< Type of default allocator
63+
using block_type = typename base_type::block_type; ///< Type of each storage block (same as T)
64+
using value_type = typename base_type::value_type; ///< Type of individual bit values (bool)
65+
using size_type = typename base_type::size_type; ///< Type used for sizes and indices
6566

6667
/**
6768
* @brief Constructs a dynamic_bitset from an input range of convertible values.
@@ -71,7 +72,9 @@ namespace sparrow
7172
* values result in set bits (1), while zero/false values result in unset bits (0).
7273
*
7374
* @tparam R Input range type that must satisfy std::ranges::input_range
75+
* @tparam A Allocator type that must satisfy sparrow::allocator
7476
* @param r The input range whose elements will be converted to bits
77+
* @param a The allocator used internally
7578
*
7679
* @pre The range elements must be convertible to value_type (bool)
7780
* @pre The range must have a computable size via std::ranges::size
@@ -82,10 +85,10 @@ namespace sparrow
8285
* dynamic_bitset<std::uint8_t> bits(values); // Results in: 10101
8386
* @endcode
8487
*/
85-
template <std::ranges::input_range R>
88+
template <std::ranges::input_range R, allocator A = default_allocator>
8689
requires std::convertible_to<std::ranges::range_value_t<R>, value_type>
87-
constexpr explicit dynamic_bitset(const R& r)
88-
: dynamic_bitset(std::ranges::size(r), true)
90+
constexpr explicit dynamic_bitset(const R& r, const A& a = A())
91+
: dynamic_bitset(std::ranges::size(r), true, a)
8992
{
9093
std::size_t i = 0;
9194
for (auto value : r)
@@ -104,7 +107,9 @@ namespace sparrow
104107
* Constructs a bitset with zero bits. The bitset can later be resized
105108
* or bits can be added using the provided methods.
106109
*/
107-
constexpr dynamic_bitset();
110+
template <class A = default_allocator>
111+
requires(not std::same_as<A, dynamic_bitset<T>> and allocator<A>)
112+
constexpr dynamic_bitset(const A& a = A());
108113

109114
/**
110115
* @brief Constructs a bitset with n bits, all initialized to false.
@@ -114,7 +119,8 @@ namespace sparrow
114119
* @post size() == n
115120
* @post All bits are set to false
116121
*/
117-
constexpr explicit dynamic_bitset(size_type n);
122+
template <allocator A = default_allocator>
123+
constexpr explicit dynamic_bitset(size_type n, const A& a = A());
118124

119125
/**
120126
* @brief Constructs a bitset with n bits, all initialized to the specified value.
@@ -125,7 +131,8 @@ namespace sparrow
125131
* @post size() == n
126132
* @post All bits are set to v
127133
*/
128-
constexpr dynamic_bitset(size_type n, value_type v);
134+
template <allocator A = default_allocator>
135+
constexpr dynamic_bitset(size_type n, value_type v, const A& a = A());
129136

130137
/**
131138
* @brief Constructs a bitset using existing memory.
@@ -142,7 +149,8 @@ namespace sparrow
142149
* @warning The caller must ensure the memory pointed to by p remains valid
143150
* and contains properly formatted bit data.
144151
*/
145-
constexpr dynamic_bitset(block_type* p, size_type n);
152+
template <allocator A = default_allocator>
153+
constexpr dynamic_bitset(block_type* p, size_type n, const A& a = A());
146154

147155
/**
148156
* @brief Constructs a bitset using existing memory with null count tracking.
@@ -159,12 +167,19 @@ namespace sparrow
159167
* @post size() == n
160168
* @post null_count() == null_count
161169
*/
162-
constexpr dynamic_bitset(block_type* p, size_type n, size_type null_count);
170+
template <allocator A = default_allocator>
171+
constexpr dynamic_bitset(block_type* p, size_type n, size_type null_count, const A& a = A());
163172

164173
constexpr ~dynamic_bitset() = default;
165174
constexpr dynamic_bitset(const dynamic_bitset&) = default;
166175
constexpr dynamic_bitset(dynamic_bitset&&) noexcept = default;
167176

177+
template <allocator A>
178+
constexpr dynamic_bitset(const dynamic_bitset& rhs, const A& a);
179+
180+
template <allocator A>
181+
constexpr dynamic_bitset(dynamic_bitset&& rhs, const A& a);
182+
168183
constexpr dynamic_bitset& operator=(const dynamic_bitset&) = default;
169184
constexpr dynamic_bitset& operator=(dynamic_bitset&&) noexcept = default;
170185

@@ -179,23 +194,27 @@ namespace sparrow
179194
};
180195

181196
template <std::integral T>
182-
constexpr dynamic_bitset<T>::dynamic_bitset()
183-
: base_type(storage_type(), 0u)
197+
template <class A>
198+
requires(not std::same_as<A, dynamic_bitset<T>> and allocator<A>)
199+
constexpr dynamic_bitset<T>::dynamic_bitset(const A& a)
200+
: base_type(storage_type(a), 0u)
184201
{
185202
base_type::zero_unused_bits();
186203
}
187204

188205
template <std::integral T>
189-
constexpr dynamic_bitset<T>::dynamic_bitset(size_type n)
190-
: dynamic_bitset(n, false)
206+
template <allocator A>
207+
constexpr dynamic_bitset<T>::dynamic_bitset(size_type n, const A& a)
208+
: dynamic_bitset(n, false, a)
191209
{
192210
base_type::zero_unused_bits();
193211
}
194212

195213
template <std::integral T>
196-
constexpr dynamic_bitset<T>::dynamic_bitset(size_type n, value_type value)
214+
template <allocator A>
215+
constexpr dynamic_bitset<T>::dynamic_bitset(size_type n, value_type value, const A& a)
197216
: base_type(
198-
storage_type(this->compute_block_count(n), value ? block_type(~block_type(0)) : block_type(0)),
217+
storage_type(this->compute_block_count(n), value ? block_type(~block_type(0)) : block_type(0), a),
199218
n,
200219
value ? 0u : n
201220
)
@@ -204,20 +223,36 @@ namespace sparrow
204223
}
205224

206225
template <std::integral T>
207-
constexpr dynamic_bitset<T>::dynamic_bitset(block_type* p, size_type n)
208-
: base_type(storage_type(p, p != nullptr ? this->compute_block_count(n) : 0), n)
226+
template <allocator A>
227+
constexpr dynamic_bitset<T>::dynamic_bitset(block_type* p, size_type n, const A& a)
228+
: base_type(storage_type(p, p != nullptr ? this->compute_block_count(n) : 0, a), n)
209229
{
210230
base_type::zero_unused_bits();
211231
}
212232

213233
template <std::integral T>
214-
constexpr dynamic_bitset<T>::dynamic_bitset(block_type* p, size_type n, size_type null_count)
215-
: base_type(storage_type(p, this->compute_block_count(n)), n, null_count)
234+
template <allocator A>
235+
constexpr dynamic_bitset<T>::dynamic_bitset(block_type* p, size_type n, size_type null_count, const A& a)
236+
: base_type(storage_type(p, this->compute_block_count(n), a), n, null_count)
216237
{
217238
base_type::zero_unused_bits();
218239
SPARROW_ASSERT_TRUE(base_type::null_count() == base_type::size() - base_type::count_non_null());
219240
}
220241

242+
template <std::integral T>
243+
template <allocator A>
244+
constexpr dynamic_bitset<T>::dynamic_bitset(const dynamic_bitset& rhs, const A& a)
245+
: base_type(storage_type(rhs, a))
246+
{
247+
}
248+
249+
template <std::integral T>
250+
template <allocator A>
251+
constexpr dynamic_bitset<T>::dynamic_bitset(dynamic_bitset&& rhs, const A& a)
252+
: base_type(storage_type(std::move(rhs), a))
253+
{
254+
}
255+
221256
/**
222257
* @brief Type alias for a validity bitmap using 8-bit storage blocks.
223258
*

include/sparrow/u8_buffer.hpp

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -107,10 +107,11 @@ namespace sparrow
107107
{
108108
public:
109109

110-
using holder_type = detail::holder<buffer<std::uint8_t>>;
111-
using buffer_adaptor_type = buffer_adaptor<T, buffer<std::uint8_t>&>;
110+
using buffer_type = buffer<std::uint8_t>;
111+
using holder_type = detail::holder<buffer_type>;
112+
using buffer_adaptor_type = buffer_adaptor<T, buffer_type&>;
112113
using holder_type::extract_storage;
113-
using default_allocator_type = std::allocator<std::uint8_t>;
114+
using default_allocator = buffer_type::default_allocator;
114115

115116
/**
116117
* Move constructor.
@@ -183,12 +184,26 @@ namespace sparrow
183184
* Especially, one should not mixed operator new[] and std::allocator, as this
184185
* later is not guaranteed to free the memory with a call to operator delete[] only.
185186
*
187+
* The recommended way to allocate data_ptr is to use the default allocator
188+
* provided by u8_buffer:
189+
* \code{.cpp}
190+
* using allocator = u8_buffer::default_allocator;
191+
* auto* data_ptr = reinterpret_cast<T*>(default_allocator().allocate(sizeof(uint8_t) * count));
192+
* u8_buffer buf(data_ptr, count);
193+
* \endcode
194+
*
195+
* However, if you prefer to provide your own allocator, you must ensure that:
196+
* - either it allocates and returns a buffer or std::uint8_t
197+
* - or it accepts a std::uint8_t* as its deallocate argument
198+
* The first option is safer and more conformant to the standard allocator,
199+
* but requires and extract cast in the client code.
200+
*
186201
* @tparam A The allocator type.
187202
* @param data_ptr Pointer to the storage.
188203
* @param count Number of elements in the storage.
189204
* @param a The allocator to use.
190205
*/
191-
template <allocator A = default_allocator_type>
206+
template <allocator A = default_allocator>
192207
constexpr u8_buffer(T* data_ptr, std::size_t count, const A& a = A());
193208
};
194209

test/test_primitive_array.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -818,7 +818,8 @@ namespace sparrow
818818
# pragma GCC diagnostic ignored "-Wcast-align"
819819
#endif
820820
size_t num_rows = 100000;
821-
uint8_t* data_ptr = std::allocator<uint8_t>().allocate(sizeof(uint64_t) * num_rows);
821+
using allocator_type = sparrow::u8_buffer<uint64_t>::default_allocator;
822+
uint8_t* data_ptr = allocator_type().allocate(sizeof(uint64_t) * num_rows);
822823
auto cast_ptr = reinterpret_cast<uint64_t*>(data_ptr);
823824
for (size_t idx = 0; idx < num_rows; ++idx)
824825
{

0 commit comments

Comments
 (0)