1 /** @file smallvector.h
2 * @brief Custom vector implementations using small vector optimisation
4 /* Copyright (C) 2012,2013,2014,2017 Olly Betts
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to
8 * deal in the Software without restriction, including without limitation the
9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 * sell copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
25 #ifndef XAPIAN_INCLUDED_SMALLVECTOR_H
26 #define XAPIAN_INCLUDED_SMALLVECTOR_H
29 #include <cstddef> // For std::size_t
30 #include <cstring> // For std::memcpy
31 #include <type_traits>
35 /** Suitable for "simple" type T.
37 * If sizeof(T) > 2 * sizeof(T*) this isn't going to work, and it's not very
38 * useful when sizeof(T) == 2 * sizeof(T*) as you can only store a single
41 * Offers optional Copy-On-Write functionality - if COW is true, then copying
42 * a Vec with external data only makes a copy of that data if you attempt
43 * to modify it. Current COW is only supported for integral types T.
47 typename
= typename
std::enable_if
<!COW
||
48 std::is_integral
<T
>::value
>::type
>
52 static constexpr std::size_t INTERNAL_CAPACITY
= 2 * sizeof(T
*) / sizeof(T
);
55 T v
[INTERNAL_CAPACITY
];
64 explicit Vec_to_copy(const Vec
& o
) : ref(o
) {}
68 typedef std::size_t size_type
;
70 typedef const T
* const_iterator
;
76 // Prevent inadvertent copying.
77 Vec(const Vec
&) = delete;
79 Vec(const Vec_to_copy
& o
) {
83 // Prevent inadvertent copying.
84 void operator=(const Vec
&) = delete;
86 void operator=(const Vec_to_copy
& o
) {
91 Vec_to_copy
copy() const {
92 return Vec_to_copy(*this);
95 Vec(Vec
&& o
) noexcept
: Vec() {
96 std::memcpy(&u
, &o
.u
, sizeof(u
));
100 void operator=(Vec
&& o
) {
106 explicit Vec(size_type n
) : c(0) {
114 size_type
size() const {
115 return is_external() ? u
.e
- u
.b
: c
;
118 size_type
capacity() const {
119 return is_external() ? c
: INTERNAL_CAPACITY
;
123 return is_external() ? u
.b
== u
.e
: c
== 0;
126 void reserve(size_type n
) {
127 if (n
> capacity()) {
132 const_iterator
cbegin() const {
133 return is_external() ? u
.b
: u
.v
;
136 const_iterator
cend() const {
137 return is_external() ? u
.e
: u
.v
+ c
;
140 const_iterator
begin() const {
144 const_iterator
end() const {
149 // FIXME: This is a bit eager - non-const begin() is often invoked when
150 // no modification is needed, but doing it lazily is a bit tricky as
151 // the pointer will change when we COW.
152 if (COW
&& is_external() && u
.b
[-1] > 0) {
155 return is_external() ? u
.b
: u
.v
;
159 if (COW
&& is_external() && u
.b
[-1] > 0) {
162 return is_external() ? u
.e
: u
.v
+ c
;
165 void push_back(T elt
) {
166 auto cap
= capacity();
170 if (c
>= INTERNAL_CAPACITY
) {
171 if (COW
&& u
.b
[-1] > 0)
193 void erase(const_iterator it
) {
196 T
* p
= const_cast<T
*>(it
);
209 void insert(const_iterator pos
, const T
& elt
) {
211 T
* p
= const_cast<T
*>(end());
215 *(const_cast<T
*>(pos
)) = elt
;
218 const T
& operator[](size_type idx
) const {
222 T
& operator[](size_type idx
) {
223 if (COW
&& is_external() && u
.b
[-1] > 0) {
226 return const_cast<T
&>(begin()[idx
]);
229 const T
& front() const {
233 const T
& back() const {
239 if (!COW
|| u
.b
[-1] == 0)
240 delete [] (u
.b
- COW
);
245 void do_reserve(size_type n
) {
246 // Logic error or size_t wrapping.
247 if (rare(COW
? n
< c
: n
<= c
))
248 throw std::bad_alloc();
249 T
* blk
= new T
[n
+ COW
];
253 u
.e
= std::copy(u
.b
, u
.e
, blk
);
256 u
.e
= std::copy(u
.v
, u
.v
+ c
, blk
);
263 T
* blk
= new T
[c
+ 1];
265 u
.e
= std::copy(u
.b
, u
.e
, blk
);
270 void do_copy_from(const Vec
& o
) {
271 if (!o
.is_external()) {
280 u
.e
= std::copy(o
.u
.b
, o
.u
.e
, blk
);
286 /// Return true if storage is external to the object.
287 bool is_external() const noexcept
{
288 return c
> INTERNAL_CAPACITY
;
293 using VecCOW
= Vec
<T
, true>;
298 static constexpr std::size_t INTERNAL_CAPACITY
= 2;
300 void * p
[INTERNAL_CAPACITY
];
303 SmallVector_() : c(0) { }
305 // Prevent inadvertent copying.
306 SmallVector_(const SmallVector_
&) = delete;
308 // Prevent inadvertent copying.
309 void operator=(const SmallVector_
&) = delete;
311 SmallVector_(SmallVector_
&& o
) noexcept
: SmallVector_() {
312 std::memcpy(p
, o
.p
, sizeof(p
));
316 explicit SmallVector_(std::size_t n
) : c(0) {
320 std::size_t size() const {
323 void * const * b
= static_cast<void * const *>(p
[0]);
324 void * const * e
= static_cast<void * const *>(p
[1]);
328 std::size_t capacity() const {
329 return is_external() ? c
: INTERNAL_CAPACITY
;
333 return is_external() ? p
[0] == p
[1] : c
== 0;
336 void reserve(std::size_t n
) {
337 if (n
> INTERNAL_CAPACITY
&& n
> c
) {
345 void do_reserve(std::size_t n
);
353 /// Return true if storage is external to the object.
354 bool is_external() const {
355 return c
> INTERNAL_CAPACITY
;
358 void * const * do_begin() const {
359 return is_external() ? static_cast<void * const *>(p
[0]) : p
;
362 void * const * do_end() const {
363 return is_external() ? static_cast<void * const *>(p
[1]) : p
+ c
;
366 void do_push_back(void* elt
) {
367 std::size_t cap
= capacity();
371 if (c
>= INTERNAL_CAPACITY
) {
372 void ** e
= static_cast<void **>(p
[1]);
374 p
[1] = static_cast<void*>(e
);
381 /** Vector of Xapian PIMPL internal objects.
383 * A notable feature is that if the vector holds <= 2 objects, there's no
384 * extra storage - the two internal pointers are held in the two
385 * pointers which otherwise point to the first and just after the last
388 * This means that for the fairly common cases of pair-wise Query operators
389 * and Database objects with one or two subdatabases, we use less space than
390 * std::vector<Xapian::Foo::Internal*> would.
392 template<typename TI
>
393 class SmallVectorI
: public SmallVector_
{
395 typedef std::size_t size_type
;
397 typedef TI
*const* const_iterator
;
399 // Create an empty SmallVectorI.
400 SmallVectorI() : SmallVector_() { }
402 // Create an empty SmallVectorI with n elements reserved.
403 explicit SmallVectorI(size_type n
) : SmallVector_(n
) { }
405 SmallVectorI(SmallVectorI
&& o
) noexcept
: SmallVector_(o
) { }
411 const_iterator
begin() const {
412 return const_iterator(do_begin());
415 const_iterator
end() const {
416 return const_iterator(do_end());
420 for (const_iterator i
= begin(); i
!= end(); ++i
)
421 if ((*i
) && --(*i
)->_refs
== 0)
427 void push_back(TI
* elt
) {
428 // Cast away potential const-ness in TI. We can only try to modify an
429 // element after casting to TI*, so this is const-safe overall.
430 do_push_back(const_cast<void*>(static_cast<const void*>(elt
)));
435 TI
* operator[](size_type idx
) const {
448 /** Vector of Xapian PIMPL objects.
450 * A notable feature is that if the vector holds <= 2 objects, there's no
451 * extra storage - the two internal pointers are held in the two
452 * pointers which otherwise point to the first and just after the last
455 * This means that for the fairly common cases of pair-wise Query operators
456 * and Database objects with one or two subdatabases, we use less space than
457 * std::vector<Xapian::Foo> would.
460 class SmallVector
: public SmallVectorI
<typename
T::Internal
> {
461 typedef SmallVectorI
<typename
T::Internal
> super
;
464 typedef std::size_t size_type
;
466 class const_iterator
{
467 typename
super::const_iterator ptr
;
470 const_iterator() : ptr(nullptr) { }
472 explicit const_iterator(typename
super::const_iterator ptr_
)
475 const_iterator
& operator++() { ++ptr
; return *this; }
477 const_iterator
operator++(int) { return const_iterator(ptr
++); }
479 T
operator*() const {
483 T
operator[](size_type idx
) const {
487 bool operator==(const const_iterator
& o
) const { return ptr
== o
.ptr
; }
489 bool operator!=(const const_iterator
& o
) const { return !(*this == o
); }
491 const_iterator
operator+(int n
) { return const_iterator(ptr
+ n
); }
493 const_iterator
operator-(int n
) { return const_iterator(ptr
- n
); }
496 // Create an empty SmallVector.
499 // Create an empty SmallVector with n elements reserved.
500 explicit SmallVector(size_type n
) : super(n
) { }
502 const_iterator
begin() const {
503 return const_iterator(super::begin());
506 const_iterator
end() const {
507 return const_iterator(super::end());
510 using super::push_back
;
512 void push_back(const T
& elt
) {
513 push_back(elt
.internal
.get());
516 T
operator[](size_type idx
) const {
531 #endif // XAPIAN_INCLUDED_SMALLVECTOR_H