xapian-core  2.0.0
smallvector.h
Go to the documentation of this file.
1 
4 /* Copyright (C) 2012-2026 Olly Betts
5  *
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:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
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
22  * IN THE SOFTWARE.
23  */
24 
25 #ifndef XAPIAN_INCLUDED_SMALLVECTOR_H
26 #define XAPIAN_INCLUDED_SMALLVECTOR_H
27 
28 #ifndef PACKAGE
29 # error config.h must be included first in each C++ source file
30 #endif
31 
32 #include <algorithm>
33 #include <cstddef> // For std::size_t
34 #include <cstring> // For std::memcpy
35 #include <type_traits>
36 
37 namespace Xapian {
38 
54 template<typename T,
55  bool COW = false,
56  bool UNIQUEPTR = false,
57  typename = typename std::enable_if_t<
58  (std::is_trivially_copyable_v<T> &&
59  (!(COW && UNIQUEPTR)) &&
60  (!UNIQUEPTR || std::is_pointer_v<T>) &&
61  (!COW || std::is_integral_v<T>))>>
62 class Vec {
63  // This gives capacity() if c > INTERNAL_CAPACITY, or size() otherwise.
64  std::size_t c = 0;
65 
66  static constexpr std::size_t INTERNAL_CAPACITY = 2 * sizeof(T*) / sizeof(T);
67 
68  union {
69  T v[INTERNAL_CAPACITY];
70  struct {
71  T* b;
72  T* e;
73  } p;
74  } u;
75 
76  // Only useful if !UNIQUEPTR, but can't be accessed without copy().
77  struct Vec_to_copy {
78  const Vec& ref;
79  explicit Vec_to_copy(const Vec& o) : ref(o) {}
80  };
81 
82  public:
83  typedef std::size_t size_type;
84 
85  typedef const T* const_iterator;
86 
87  typedef T* iterator;
88 
89  Vec() { }
90 
91  // Prevent inadvertent copying.
92  Vec(const Vec&) = delete;
93 
94  // Allow moving.
95 // Vec(const Vec&&) = default;
96 
97  // Only useful if !UNIQUEPTR, but can't be accessed without copy().
98  Vec(const Vec_to_copy& o) {
99  do_copy_from(o.ref);
100  }
101 
102  // Prevent inadvertent copying.
103  void operator=(const Vec&) = delete;
104 
105  // Only useful if !UNIQUEPTR, but can't be accessed without copy().
106  void operator=(const Vec_to_copy& o) {
107  clear();
108  do_copy_from(o.ref);
109  }
110 
111  template<bool ENABLE = !UNIQUEPTR>
112  auto copy() const -> typename std::enable_if_t<ENABLE, Vec_to_copy> {
113  return Vec_to_copy(*this);
114  }
115 
116  Vec(Vec&& o) noexcept : Vec() {
117  std::swap(c, o.c);
118  if (c) u = o.u;
119  }
120 
121  void operator=(Vec&& o) {
122  clear();
123  std::swap(c, o.c);
124  if (c) u = o.u;
125  }
126 
127  explicit Vec(size_type n) {
128  reserve(n);
129  }
130 
131  ~Vec() {
132  clear();
133  }
134 
135  size_type size() const {
136  return is_external() ? u.p.e - u.p.b : c;
137  }
138 
139  size_type capacity() const {
140  return is_external() ? c : INTERNAL_CAPACITY;
141  }
142 
143  bool empty() const {
144  return is_external() ? u.p.b == u.p.e : c == 0;
145  }
146 
147  void reserve(size_type n) {
148  if (n > capacity()) {
149  do_reserve(n);
150  }
151  }
152 
154  return is_external() ? u.p.b : u.v;
155  }
156 
158  return is_external() ? u.p.e : u.v + c;
159  }
160 
162  return cbegin();
163  }
164 
165  const_iterator end() const {
166  return cend();
167  }
168 
170  // FIXME: This is a bit eager - non-const begin() is often invoked when
171  // no modification is needed, but doing it lazily is a bit tricky as
172  // the pointer will change when we COW.
173  if constexpr(COW) {
174  if (is_external() && u.p.b[-1] > 0) {
175  do_cow();
176  }
177  }
178  return is_external() ? u.p.b : u.v;
179  }
180 
182  if constexpr(COW) {
183  if (is_external() && u.p.b[-1] > 0) {
184  do_cow();
185  }
186  }
187  return is_external() ? u.p.e : u.v + c;
188  }
189 
190  void push_back(T elt) {
191  auto cap = capacity();
192  if (size() == cap) {
193  do_reserve(cap * 2);
194  }
195  if (c >= INTERNAL_CAPACITY) {
196  if constexpr(COW) {
197  if (u.p.b[-1] > 0)
198  do_cow();
199  }
200  *(u.p.e++) = elt;
201  } else {
202  u.v[c++] = elt;
203  }
204  }
205 
206  void pop_back() {
207  if (is_external()) {
208  if constexpr(COW) {
209  if (u.p.b[-1] > 0) {
210  do_cow();
211  }
212  }
213  --u.p.e;
214  } else {
215  --c;
216  }
217  }
218 
219  void clear() {
220  if constexpr(UNIQUEPTR) {
221  for (const_iterator i = begin(); i != end(); ++i)
222  delete *i;
223  }
224  if (is_external())
225  do_free();
226  c = 0;
227  }
228 
230  if constexpr(COW) {
231  if (is_external() && u.p.b[-1] > 0) {
232  auto i = it - u.p.b;
233  do_cow();
234  it = u.p.b + i;
235  }
236  }
237  if constexpr(UNIQUEPTR) {
238  delete *it;
239  }
240  T* p = const_cast<T*>(it);
241  std::memmove(p, p + 1, (end() - it - 1) * sizeof(T));
242  if (is_external()) {
243  --u.p.e;
244  } else {
245  --c;
246  }
247  }
248 
250  auto n_erased = e - b;
251  if (n_erased == 0) return;
252  if constexpr(COW) {
253  if (is_external() && u.p.b[-1] > 0) {
254  auto i = b - u.p.b;
255  do_cow();
256  b = u.p.b + i;
257  e = b + n_erased;
258  }
259  }
260  if constexpr(UNIQUEPTR) {
261  for (const_iterator i = b; i != e; ++i)
262  delete *i;
263  }
264  std::memmove(const_cast<T*>(b), const_cast<T*>(e),
265  (end() - e) * sizeof(T));
266  if (is_external()) {
267  u.p.e -= n_erased;
268  } else {
269  c -= n_erased;
270  }
271  }
272 
273  void insert(const_iterator pos, const T& elt) {
274  // Optimised to reduce copying when we need to reallocate.
275  T* blk = nullptr;
276  auto cap = capacity();
277  auto n = size();
278  if (n == cap || (COW && is_external() && u.p.b[-1] > 0)) {
279  if (n == cap) {
280  cap *= 2;
281  // Logic error or size_t wrapping.
282  if (rare(COW ? cap < c : cap <= c))
283  throw std::bad_alloc();
284  }
285  blk = new T[cap + COW];
286  if constexpr(COW)
287  *blk++ = 0;
288  }
289 
290  auto b = cbegin();
291  auto e = cend();
292  T* db;
293  if (blk) {
294  // Copy elements before the insertion point to the new block.
295  std::copy(b, pos, blk);
296  db = blk;
297  } else {
298  db = (is_external() ? u.p.b : u.v);
299  }
300  // Copy elements after the insertion point.
301  std::copy_backward(pos, e, db + n + 1);
302 
303  // Insert the new element.
304  db[pos - b] = elt;
305 
306  if (blk) {
307  if (is_external()) {
308  do_free();
309  }
310  u.p.b = blk;
311  u.p.e = blk + n + 1;
312  c = cap;
313  } else if (is_external()) {
314  ++u.p.e;
315  } else {
316  ++c;
317  }
318  }
319 
320  const T& operator[](size_type idx) const {
321  return begin()[idx];
322  }
323 
325  if constexpr(COW) {
326  if (is_external() && u.p.b[-1] > 0) {
327  do_cow();
328  }
329  }
330  return const_cast<T&>(begin()[idx]);
331  }
332 
333  const T& front() const {
334  return *(begin());
335  }
336 
337  const T& back() const {
338  return end()[-1];
339  }
340 
341  // Like `v[idx].release()` for `std::vector<std::unique_ptr<T>> v`.
342  template<bool ENABLE = !UNIQUEPTR>
344  {
345  T r = nullptr;
346  std::swap(r, begin()[idx]);
347  return r;
348  }
349 
350  protected:
351  void do_free() {
352  if constexpr(COW) {
353  if (u.p.b[-1] > 0)
354  --u.p.b[-1];
355  else
356  delete [] (u.p.b - 1);
357  } else {
358  delete [] u.p.b;
359  }
360  }
361 
363  // Logic error or size_t wrapping.
364  if (rare(COW ? n < c : n <= c))
365  throw std::bad_alloc();
366  T* blk = new T[n + COW];
367  if constexpr(COW)
368  *blk++ = 0;
369  if (is_external()) {
370  u.p.e = std::copy(u.p.b, u.p.e, blk);
371  do_free();
372  } else {
373  u.p.e = std::copy(u.v, u.v + c, blk);
374  }
375  u.p.b = blk;
376  c = n;
377  }
378 
379  void do_cow() {
380  T* blk = new T[c + 1];
381  *blk++ = 0;
382  u.p.e = std::copy(u.p.b, u.p.e, blk);
383  --u.p.b[-1];
384  u.p.b = blk;
385  }
386 
387  void do_copy_from(const Vec& o) {
388  c = o.c;
389  if (!o.is_external()) {
390  if (c) u = o.u;
391  } else if constexpr(COW) {
392  u = o.u;
393  ++u.p.b[-1];
394  } else {
395  T* blk = new T[c];
396  u.p.e = std::copy(o.u.p.b, o.u.p.e, blk);
397  u.p.b = blk;
398  }
399  }
400 
402  bool is_external() const noexcept {
403  return c > INTERNAL_CAPACITY;
404  }
405 };
406 
407 template<typename T>
409 
410 template<typename T>
412 
414  std::size_t c = 0;
415 
416  static constexpr std::size_t INTERNAL_CAPACITY = 2;
417 
419 
420  public:
422 
423  // Prevent inadvertent copying.
424  SmallVector_(const SmallVector_&) = delete;
425 
426  // Prevent inadvertent copying.
427  void operator=(const SmallVector_&) = delete;
428 
430  std::memcpy(p, o.p, sizeof(p));
431  std::swap(c, o.c);
432  }
433 
434  explicit SmallVector_(std::size_t n) {
435  reserve(n);
436  }
437 
438  std::size_t size() const {
439  if (!is_external())
440  return c;
441  void * const * b = static_cast<void * const *>(p[0]);
442  void * const * e = static_cast<void * const *>(p[1]);
443  return e - b;
444  }
445 
446  std::size_t capacity() const {
447  return is_external() ? c : INTERNAL_CAPACITY;
448  }
449 
450  bool empty() const {
451  return is_external() ? p[0] == p[1] : c == 0;
452  }
453 
454  void reserve(std::size_t n) {
455  if (n > INTERNAL_CAPACITY && n > c) {
456  do_reserve(n);
457  }
458  }
459 
460  protected:
461  void do_free();
462 
463  void do_reserve(std::size_t n);
464 
465  void do_clear() {
466  if (is_external())
467  do_free();
468  c = 0;
469  }
470 
472  bool is_external() const {
473  return c > INTERNAL_CAPACITY;
474  }
475 
476  void * const * do_begin() const {
477  return is_external() ? static_cast<void * const *>(p[0]) : p;
478  }
479 
480  void * const * do_end() const {
481  return is_external() ? static_cast<void * const *>(p[1]) : p + c;
482  }
483 
484  void do_push_back(void* elt) {
485  std::size_t cap = capacity();
486  if (size() == cap) {
487  do_reserve(cap * 2);
488  }
489  if (c >= INTERNAL_CAPACITY) {
490  void ** e = static_cast<void **>(p[1]);
491  *e++ = elt;
492  p[1] = static_cast<void*>(e);
493  } else {
494  p[c++] = elt;
495  }
496  }
497 };
498 
510 template<typename TI>
511 class SmallVectorI : public SmallVector_ {
512  public:
513  typedef std::size_t size_type;
514 
515  typedef TI*const* const_iterator;
516 
517  // Create an empty SmallVectorI.
519 
520  // Create an empty SmallVectorI with n elements reserved.
521  explicit SmallVectorI(size_type n) : SmallVector_(n) { }
522 
523  SmallVectorI(SmallVectorI&& o) noexcept : SmallVector_(o) { }
524 
526  clear();
527  }
528 
530  return const_iterator(do_begin());
531  }
532 
533  const_iterator end() const {
534  return const_iterator(do_end());
535  }
536 
537  void clear() {
538  for (const_iterator i = begin(); i != end(); ++i)
539  if ((*i) && --(*i)->_refs == 0)
540  delete *i;
541 
542  do_clear();
543  }
544 
545  void push_back(TI* elt) {
546  // Cast away potential const-ness in TI. We can only try to modify an
547  // element after casting to TI*, so this is const-safe overall.
548  do_push_back(const_cast<void*>(static_cast<const void*>(elt)));
549  if (elt)
550  ++elt->_refs;
551  }
552 
553  TI* operator[](size_type idx) const {
554  return begin()[idx];
555  }
556 
557  TI* front() const {
558  return *(begin());
559  }
560 
561  TI* back() const {
562  return end()[-1];
563  }
564 };
565 
577 template<typename T>
578 class SmallVector : public SmallVectorI<typename T::Internal> {
580 
581  public:
582  typedef std::size_t size_type;
583 
586 
587  public:
588  const_iterator() : ptr(nullptr) { }
589 
590  explicit const_iterator(typename super::const_iterator ptr_)
591  : ptr(ptr_) { }
592 
593  const_iterator & operator++() { ++ptr; return *this; }
594 
596 
597  T operator*() const {
598  return T(*ptr);
599  }
600 
601  T operator[](size_type idx) const {
602  return T(ptr[idx]);
603  }
604 
605  bool operator==(const const_iterator& o) const { return ptr == o.ptr; }
606 
607  bool operator!=(const const_iterator& o) const { return !(*this == o); }
608 
609  template<typename I, typename = std::enable_if_t<std::is_integral_v<I>>>
611 
612  template<typename I, typename = std::enable_if_t<std::is_integral_v<I>>>
614  };
615 
616  // Create an empty SmallVector.
618 
619  // Create an empty SmallVector with n elements reserved.
620  explicit SmallVector(size_type n) : super(n) { }
621 
623  return const_iterator(super::begin());
624  }
625 
626  const_iterator end() const {
627  return const_iterator(super::end());
628  }
629 
631 
632  void push_back(const T & elt) {
633  push_back(elt.internal.get());
634  }
635 
636  T operator[](size_type idx) const {
637  return begin()[idx];
638  }
639 
640  T front() const {
641  return *(begin());
642  }
643 
644  T back() const {
645  return end()[-1];
646  }
647 };
648 
649 }
650 
651 #endif // XAPIAN_INCLUDED_SMALLVECTOR_H
Vector of Xapian PIMPL internal objects.
Definition: smallvector.h:511
const_iterator end() const
Definition: smallvector.h:533
TI *const * const_iterator
Definition: smallvector.h:515
void push_back(TI *elt)
Definition: smallvector.h:545
TI * front() const
Definition: smallvector.h:557
TI * back() const
Definition: smallvector.h:561
const_iterator begin() const
Definition: smallvector.h:529
TI * operator[](size_type idx) const
Definition: smallvector.h:553
SmallVectorI(size_type n)
Definition: smallvector.h:521
SmallVectorI(SmallVectorI &&o) noexcept
Definition: smallvector.h:523
std::size_t size_type
Definition: smallvector.h:513
bool operator==(const const_iterator &o) const
Definition: smallvector.h:605
T operator[](size_type idx) const
Definition: smallvector.h:601
const_iterator(typename super::const_iterator ptr_)
Definition: smallvector.h:590
bool operator!=(const const_iterator &o) const
Definition: smallvector.h:607
void do_push_back(void *elt)
Definition: smallvector.h:484
bool empty() const
Definition: smallvector.h:450
SmallVector_(std::size_t n)
Definition: smallvector.h:434
std::size_t capacity() const
Definition: smallvector.h:446
void * p[INTERNAL_CAPACITY]
Definition: smallvector.h:418
SmallVector_(const SmallVector_ &)=delete
void operator=(const SmallVector_ &)=delete
static constexpr std::size_t INTERNAL_CAPACITY
Definition: smallvector.h:416
void *const * do_end() const
Definition: smallvector.h:480
std::size_t size() const
Definition: smallvector.h:438
void reserve(std::size_t n)
Definition: smallvector.h:454
void do_reserve(std::size_t n)
Definition: smallvector.cc:32
void *const * do_begin() const
Definition: smallvector.h:476
SmallVector_(SmallVector_ &&o) noexcept
Definition: smallvector.h:429
bool is_external() const
Return true if storage is external to the object.
Definition: smallvector.h:472
Vector of Xapian PIMPL objects.
Definition: smallvector.h:578
SmallVector(size_type n)
Definition: smallvector.h:620
SmallVectorI< typename T::Internal > super
Definition: smallvector.h:579
std::size_t size_type
Definition: smallvector.h:582
T operator[](size_type idx) const
Definition: smallvector.h:636
const_iterator begin() const
Definition: smallvector.h:622
void push_back(TI *elt)
Definition: smallvector.h:545
const_iterator end() const
Definition: smallvector.h:626
void push_back(const T &elt)
Definition: smallvector.h:632
Suitable for "simple" type T.
Definition: smallvector.h:62
const_iterator end() const
Definition: smallvector.h:165
void operator=(Vec &&o)
Definition: smallvector.h:121
iterator begin()
Definition: smallvector.h:169
Vec(size_type n)
Definition: smallvector.h:127
const T * const_iterator
Definition: smallvector.h:85
auto copy() const -> typename std::enable_if_t< ENABLE, Vec_to_copy >
Definition: smallvector.h:112
bool is_external() const noexcept
Return true if storage is external to the object.
Definition: smallvector.h:402
struct Xapian::Vec::@1::@2 p
T release_at(size_type idx)
Definition: smallvector.h:343
void reserve(size_type n)
Definition: smallvector.h:147
void erase(const_iterator it)
Definition: smallvector.h:229
void erase(const_iterator b, const_iterator e)
Definition: smallvector.h:249
void clear()
Definition: smallvector.h:219
void do_copy_from(const Vec &o)
Definition: smallvector.h:387
union Xapian::Vec::@1 u
const T & front() const
Definition: smallvector.h:333
void insert(const_iterator pos, const T &elt)
Definition: smallvector.h:273
std::size_t c
Definition: smallvector.h:64
void operator=(const Vec_to_copy &o)
Definition: smallvector.h:106
Vec(Vec &&o) noexcept
Definition: smallvector.h:116
Vec(const Vec &)=delete
Vec(const Vec_to_copy &o)
Definition: smallvector.h:98
const T & back() const
Definition: smallvector.h:337
size_type capacity() const
Definition: smallvector.h:139
std::size_t size_type
Definition: smallvector.h:83
void push_back(T elt)
Definition: smallvector.h:190
T & operator[](size_type idx)
Definition: smallvector.h:324
const_iterator begin() const
Definition: smallvector.h:161
size_type size() const
Definition: smallvector.h:135
void do_free()
Definition: smallvector.h:351
void do_cow()
Definition: smallvector.h:379
iterator end()
Definition: smallvector.h:181
bool empty() const
Definition: smallvector.h:143
const_iterator cend() const
Definition: smallvector.h:157
const_iterator cbegin() const
Definition: smallvector.h:153
const T & operator[](size_type idx) const
Definition: smallvector.h:320
void do_reserve(size_type n)
Definition: smallvector.h:362
void pop_back()
Definition: smallvector.h:206
void operator=(const Vec &)=delete
#define rare(COND)
Definition: config.h:607
PositionList * p
Xapian::termpos pos
The Xapian namespace contains public interfaces for the Xapian library.
Definition: compactor.cc:82
Vec_to_copy(const Vec &o)
Definition: smallvector.h:79