1 /* Copyright 2015 The TensorFlow Authors. All Rights Reserved.
2
3 Licensed under the Apache License, Version 2.0 (the "License");
4 you may not use this file except in compliance with the License.
5 You may obtain a copy of the License at
6
7 http://www.apache.org/licenses/LICENSE-2.0
8
9 Unless required by applicable law or agreed to in writing, software
10 distributed under the License is distributed on an "AS IS" BASIS,
11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 See the License for the specific language governing permissions and
13 limitations under the License.
14 ==============================================================================*/
15
16 // An InlinedVector<T,N,A> is like a std::vector<T,A>, except that storage
17 // for sequences of length <= N are provided inline without requiring
18 // any heap allocation. Typically N is very small (e.g., 4) so that
19 // sequences that are expected to be short do not require allocations.
20 //
21 // Only some of the std::vector<> operations are currently implemented.
22 // Other operations may be added as needed to facilitate migrating
23 // code that uses std::vector<> to InlinedVector<>.
24 //
25 // NOTE: If you want an inlined version to replace use of a
26 // std::vector<bool>, consider using util::bitmap::InlinedBitVector<NBITS>
27 // in util/bitmap/inlined_bitvector.h
28 //
29 // TODO(billydonahue): change size_t to size_type where appropriate.
30
31 #ifndef TENSORFLOW_LIB_GTL_INLINED_VECTOR_H_
32 #define TENSORFLOW_LIB_GTL_INLINED_VECTOR_H_
33
34 #include <stddef.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <sys/types.h>
38 #include <algorithm>
39 #include <cstddef>
40 #include <iterator>
41 #include <memory>
42 #include <type_traits>
43 #include <vector>
44
45 #include "tensorflow/core/lib/gtl/manual_constructor.h"
46 #include "tensorflow/core/platform/cpu_info.h"
47 #include "tensorflow/core/platform/logging.h"
48 #include "tensorflow/core/platform/mem.h"
49 #include "tensorflow/core/platform/types.h"
50
51 #include <initializer_list> // NOLINT(build/include_order)
52
53 namespace tensorflow {
54 namespace gtl {
55
56 template <typename T, int N>
57 class InlinedVector {
58 public:
59 typedef T value_type;
60 typedef T* pointer;
61 typedef const T* const_pointer;
62 typedef T& reference;
63 typedef const T& const_reference;
64 typedef size_t size_type;
65 typedef std::ptrdiff_t difference_type;
66 typedef pointer iterator;
67 typedef const_pointer const_iterator;
68
69 // Create an empty vector
70 InlinedVector();
71
72 // Create a vector with n copies of value_type().
73 explicit InlinedVector(size_t n);
74
75 // Create a vector with n copies of elem
76 InlinedVector(size_t n, const value_type& elem);
77
78 // Create and initialize with the elements [range_start .. range_end).
79 // The unused enable_if argument restricts this constructor so that it is
80 // elided when value_type is an integral type. This prevents ambiguous
81 // interpretation between a call to this constructor with two integral
82 // arguments and a call to the preceding (n, elem) constructor.
83 template <typename InputIterator>
84 InlinedVector(
85 InputIterator range_start, InputIterator range_end,
86 typename std::enable_if<!std::is_integral<InputIterator>::value>::type* =
87 NULL) {
88 InitRep();
89 AppendRange(range_start, range_end);
90 }
91
InlinedVector(std::initializer_list<value_type> init)92 InlinedVector(std::initializer_list<value_type> init) {
93 InitRep();
94 AppendRange(init.begin(), init.end());
95 }
96
97 InlinedVector(const InlinedVector& v);
98
~InlinedVector()99 ~InlinedVector() { clear(); }
100
101 InlinedVector& operator=(const InlinedVector& v) {
102 // Optimized to avoid reallocation.
103 // Prefer reassignment to copy construction for elements.
104 const size_t s = size();
105 const size_t vs = v.size();
106 if (s < vs) { // grow
107 reserve(vs);
108 if (s) std::copy(v.begin(), v.begin() + s, begin());
109 std::copy(v.begin() + s, v.end(), std::back_inserter(*this));
110 } else { // maybe shrink
111 erase(begin() + vs, end());
112 std::copy(v.begin(), v.end(), begin());
113 }
114 return *this;
115 }
116
size()117 size_t size() const { return size_internal(); }
118
empty()119 bool empty() const { return (size() == 0); }
120
121 // Return number of elements that can be stored in vector
122 // without requiring a reallocation of underlying memory
capacity()123 size_t capacity() const {
124 if (is_inline()) {
125 return kFit;
126 } else {
127 return static_cast<size_t>(1) << u_.data[kSize - 2];
128 }
129 }
130
131 // Return a pointer to the underlying array.
132 // Only result[0,size()-1] are defined.
data()133 pointer data() {
134 if (is_inline()) {
135 return reinterpret_cast<T*>(u_.data);
136 } else {
137 return outofline_pointer();
138 }
139 }
data()140 const_pointer data() const {
141 return const_cast<InlinedVector<T, N>*>(this)->data();
142 }
143
144 // Remove all elements
clear()145 void clear() {
146 DiscardStorage();
147 u_.data[kSize - 1] = 0;
148 }
149
150 // Return the ith element
151 // REQUIRES: 0 <= i < size()
at(size_t i)152 const value_type& at(size_t i) const {
153 DCHECK_LT(i, size());
154 return data()[i];
155 }
156 const value_type& operator[](size_t i) const {
157 DCHECK_LT(i, size());
158 return data()[i];
159 }
160
161 // Return a non-const reference to the ith element
162 // REQUIRES: 0 <= i < size()
at(size_t i)163 value_type& at(size_t i) {
164 DCHECK_LT(i, size());
165 return data()[i];
166 }
167 value_type& operator[](size_t i) {
168 DCHECK_LT(i, size());
169 return data()[i];
170 }
171
back()172 value_type& back() {
173 DCHECK(!empty());
174 return at(size() - 1);
175 }
176
back()177 const value_type& back() const {
178 DCHECK(!empty());
179 return at(size() - 1);
180 }
181
front()182 value_type& front() {
183 DCHECK(!empty());
184 return at(0);
185 }
186
front()187 const value_type& front() const {
188 DCHECK(!empty());
189 return at(0);
190 }
191
192 // Append a T constructed with args to the vector.
193 // Increases size() by one.
194 // Amortized complexity: O(1)
195 // Worst-case complexity: O(size())
196 template <typename... Args>
emplace_back(Args &&...args)197 void emplace_back(Args&&... args) {
198 size_t s = size();
199 DCHECK_LE(s, capacity());
200 if (s < capacity()) {
201 new (data() + s) T(std::forward<Args>(args)...);
202 set_size_internal(s + 1);
203 } else {
204 EmplaceBackSlow(std::forward<Args>(args)...);
205 }
206 }
207
208 // Append t to the vector.
209 // Increases size() by one.
210 // Amortized complexity: O(1)
211 // Worst-case complexity: O(size())
push_back(const value_type & t)212 void push_back(const value_type& t) { emplace_back(t); }
push_back(value_type && t)213 void push_back(value_type&& t) { emplace_back(std::move(t)); }
214
pop_back()215 inline void pop_back() {
216 DCHECK(!empty());
217 const size_t s = size();
218 Destroy(data() + s - 1, 1);
219 set_size_internal(s - 1);
220 }
221
222 // Resizes the vector to contain "n" elements.
223 // If "n" is smaller than the initial size, extra elements are destroyed.
224 // If "n" is larger than the initial size, enough copies of "elem"
225 // are appended to increase the size to "n". If "elem" is omitted,
226 // new elements are value-initialized.
resize(size_t n)227 void resize(size_t n) { Resize<ValueInit>(n, nullptr); }
resize(size_t n,const value_type & elem)228 void resize(size_t n, const value_type& elem) { Resize<Fill>(n, &elem); }
229
begin()230 iterator begin() { return data(); }
begin()231 const_iterator begin() const { return data(); }
232
end()233 iterator end() { return data() + size(); }
end()234 const_iterator end() const { return data() + size(); }
235
236 iterator insert(iterator pos, const value_type& v);
237
erase(iterator pos)238 iterator erase(iterator pos) {
239 DCHECK_LT(pos, end());
240 DCHECK_GE(pos, begin());
241 std::copy(pos + 1, end(), pos);
242 pop_back();
243 return pos;
244 }
245
246 iterator erase(iterator first, iterator last);
247
248 // Enlarges the underlying representation so it can hold at least
249 // "n" elements without reallocation.
250 // Does not change size() or the actual contents of the vector.
reserve(size_t n)251 void reserve(size_t n) {
252 if (n > capacity()) {
253 // Make room for new elements
254 Grow<Move>(n);
255 }
256 }
257
258 // Swap the contents of *this with other.
259 // REQUIRES: value_type is swappable and copyable.
260 void swap(InlinedVector& other);
261
262 private:
263 // Representation can either be inlined or out-of-line.
264 // In either case, at least sizeof(void*) + 8 bytes are available.
265 //
266 // Inlined:
267 // Last byte holds the length.
268 // First (length*sizeof(T)) bytes stores the elements.
269 // Outlined:
270 // Last byte holds kSentinel.
271 // Second-last byte holds lg(capacity)
272 // Preceding 6 bytes hold size.
273 // First sizeof(T*) bytes hold pointer.
274
275 // Compute rep size.
276 static const size_t kSizeUnaligned = N * sizeof(T) + 1; // Room for tag
277 static const size_t kSize = ((kSizeUnaligned + 15) / 16) * 16; // Align
278
279 // See how many fit T we can fit inside kSize, but no more than 254
280 // since 255 is used as sentinel tag for out-of-line allocation.
281 static const unsigned int kSentinel = 255;
282 static const size_t kFit1 = (kSize - 1) / sizeof(T);
283 static const size_t kFit = (kFit1 >= kSentinel) ? (kSentinel - 1) : kFit1;
284
285 union {
286 unsigned char data[kSize];
287 // Force data to be aligned enough for a pointer.
288 T* unused_aligner;
289 } u_;
290
InitRep()291 inline void InitRep() { u_.data[kSize - 1] = 0; }
is_inline()292 inline bool is_inline() const { return u_.data[kSize - 1] != kSentinel; }
293
outofline_pointer()294 inline T* outofline_pointer() const {
295 T* ptr;
296 memcpy(&ptr, &u_.data[0], sizeof(ptr));
297 return ptr;
298 }
299
set_outofline_pointer(T * p)300 inline void set_outofline_pointer(T* p) {
301 memcpy(&u_.data[0], &p, sizeof(p));
302 }
303
outofline_word()304 inline uint64_t outofline_word() const {
305 uint64_t word;
306 memcpy(&word, &u_.data[kSize - 8], sizeof(word));
307 return word;
308 }
309
set_outofline_word(uint64_t w)310 inline void set_outofline_word(uint64_t w) {
311 memcpy(&u_.data[kSize - 8], &w, sizeof(w));
312 }
313
size_internal()314 inline size_t size_internal() const {
315 uint8_t s = static_cast<uint8_t>(u_.data[kSize - 1]);
316 if (s != kSentinel) {
317 return static_cast<size_t>(s);
318 } else {
319 const uint64_t word = outofline_word();
320 if (port::kLittleEndian) {
321 // The sentinel and capacity bits are most-significant bits in word.
322 return static_cast<size_t>(word & 0xffffffffffffull);
323 } else {
324 // The sentinel and capacity bits are least-significant bits in word.
325 return static_cast<size_t>(word >> 16);
326 }
327 }
328 }
329
set_size_internal(size_t n)330 void set_size_internal(size_t n) {
331 if (is_inline()) {
332 DCHECK_LT(n, kSentinel);
333 u_.data[kSize - 1] = static_cast<unsigned char>(n);
334 } else {
335 uint64_t word;
336 if (port::kLittleEndian) {
337 // The sentinel and capacity bits are most-significant bits in word.
338 word = (static_cast<uint64_t>(n) |
339 (static_cast<uint64_t>(u_.data[kSize - 2]) << 48) |
340 (static_cast<uint64_t>(kSentinel) << 56));
341 } else {
342 // The sentinel and capacity bits are least-significant bits in word.
343 word = ((static_cast<uint64_t>(n) << 16) |
344 (static_cast<uint64_t>(u_.data[kSize - 2]) << 8) |
345 (static_cast<uint64_t>(kSentinel)));
346 }
347 set_outofline_word(word);
348 DCHECK_EQ(u_.data[kSize - 1], kSentinel) << n;
349 }
350 }
351
DiscardStorage()352 void DiscardStorage() {
353 T* base = data();
354 size_t n = size();
355 Destroy(base, n);
356 if (!is_inline()) {
357 port::Free(base);
358 }
359 }
360
361 template <typename... Args>
EmplaceBackSlow(Args &&...args)362 void EmplaceBackSlow(Args&&... args) {
363 const size_t s = size();
364 DCHECK_EQ(s, capacity());
365 Grow<Move, Construct>(s + 1, std::forward<Args>(args)...);
366 set_size_internal(s + 1);
367 }
368
369 // Movers for Grow
370 // Does nothing.
Nop(T * src,size_t n,T * dst)371 static void Nop(T* src, size_t n, T* dst) {}
372
373 // Moves srcs[0,n-1] contents to dst[0,n-1].
Move(T * src,size_t n,T * dst)374 static void Move(T* src, size_t n, T* dst) {
375 for (size_t i = 0; i < n; i++) {
376 new (dst + i) T(std::move(*(src + i)));
377 }
378 }
379
380 // Initializers for Resize.
381 // Initializes dst[0,n-1] with empty constructor.
ValueInit(const T *,size_t n,T * dst)382 static void ValueInit(const T*, size_t n, T* dst) {
383 for (size_t i = 0; i < n; i++) {
384 new (dst + i) T();
385 }
386 }
387
388 // Initializes dst[0,n-1] with copies of *src.
Fill(const T * src,size_t n,T * dst)389 static void Fill(const T* src, size_t n, T* dst) {
390 for (size_t i = 0; i < n; i++) {
391 new (dst + i) T(*src);
392 }
393 }
394
Destroy(T * src,int n)395 void Destroy(T* src, int n) {
396 if (!std::is_trivially_destructible<T>::value) {
397 for (int i = 0; i < n; i++) {
398 (src + i)->~T();
399 }
400 }
401 }
402
403 // Initialization methods for Grow.
404 // 1) Leave uninitialized memory.
405 struct Uninitialized {
operatorUninitialized406 void operator()(T*) const {}
407 };
408 // 2) Construct a T with args at not-yet-initialized memory pointed by dst.
409 struct Construct {
410 template <class... Args>
operatorConstruct411 void operator()(T* dst, Args&&... args) const {
412 new (dst) T(std::forward<Args>(args)...);
413 }
414 };
415
416 // Grow so that capacity >= n. Uses Mover to move existing elements
417 // to new buffer, and possibly initialize the new element according
418 // to InitType.
419 // We pass the InitType and Mover as template arguments so that
420 // this code compiles even if T does not support copying or default
421 // construction.
422 template <void(Mover)(T*, size_t, T*), class InitType = Uninitialized,
423 class... Args>
Grow(size_t n,Args &&...args)424 void Grow(size_t n, Args&&... args) {
425 size_t s = size();
426 DCHECK_LE(s, capacity());
427
428 // Compute new capacity by repeatedly doubling current capacity
429 size_t target = 1;
430 size_t target_lg = 0;
431 while (target < kFit || target < n) {
432 // TODO(psrc): Check and avoid overflow?
433 target_lg++;
434 target <<= 1;
435 }
436
437 T* src = data();
438 T* dst = static_cast<T*>(port::Malloc(target * sizeof(T)));
439
440 // Need to copy elem before discarding src since it might alias src.
441 InitType{}(dst + s, std::forward<Args>(args)...);
442 Mover(src, s, dst);
443 DiscardStorage();
444
445 u_.data[kSize - 1] = kSentinel;
446 u_.data[kSize - 2] = static_cast<unsigned char>(target_lg);
447 set_size_internal(s);
448 DCHECK_EQ(capacity(), target);
449 set_outofline_pointer(dst);
450 }
451
452 // Resize to size n. Any new elements are initialized by passing
453 // elem and the destination to Initializer. We pass the Initializer
454 // as a template argument so that this code compiles even if T does
455 // not support copying.
456 template <void(Initializer)(const T*, size_t, T*)>
Resize(size_t n,const T * elem)457 void Resize(size_t n, const T* elem) {
458 size_t s = size();
459 if (n <= s) {
460 Destroy(data() + n, s - n);
461 set_size_internal(n);
462 return;
463 }
464 reserve(n);
465 DCHECK_GE(capacity(), n);
466 set_size_internal(n);
467 Initializer(elem, n - s, data() + s);
468 }
469
470 template <typename Iter>
471 void AppendRange(Iter first, Iter last, std::input_iterator_tag);
472
473 // Faster path for forward iterators.
474 template <typename Iter>
475 void AppendRange(Iter first, Iter last, std::forward_iterator_tag);
476
477 template <typename Iter>
478 void AppendRange(Iter first, Iter last);
479 };
480
481 // Provide linkage for constants.
482 template <typename T, int N>
483 const size_t InlinedVector<T, N>::kSizeUnaligned;
484 template <typename T, int N>
485 const size_t InlinedVector<T, N>::kSize;
486 template <typename T, int N>
487 const unsigned int InlinedVector<T, N>::kSentinel;
488 template <typename T, int N>
489 const size_t InlinedVector<T, N>::kFit1;
490 template <typename T, int N>
491 const size_t InlinedVector<T, N>::kFit;
492
493 template <typename T, int N>
swap(InlinedVector<T,N> & a,InlinedVector<T,N> & b)494 inline void swap(InlinedVector<T, N>& a, InlinedVector<T, N>& b) {
495 a.swap(b);
496 }
497
498 template <typename T, int N>
499 inline bool operator==(const InlinedVector<T, N>& a,
500 const InlinedVector<T, N>& b) {
501 return a.size() == b.size() && std::equal(a.begin(), a.end(), b.begin());
502 }
503
504 template <typename T, int N>
505 inline bool operator!=(const InlinedVector<T, N>& a,
506 const InlinedVector<T, N>& b) {
507 return !(a == b);
508 }
509
510 template <typename T, int N>
511 inline bool operator<(const InlinedVector<T, N>& a,
512 const InlinedVector<T, N>& b) {
513 return std::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end());
514 }
515
516 template <typename T, int N>
517 inline bool operator>(const InlinedVector<T, N>& a,
518 const InlinedVector<T, N>& b) {
519 return b < a;
520 }
521
522 template <typename T, int N>
523 inline bool operator<=(const InlinedVector<T, N>& a,
524 const InlinedVector<T, N>& b) {
525 return !(b < a);
526 }
527
528 template <typename T, int N>
529 inline bool operator>=(const InlinedVector<T, N>& a,
530 const InlinedVector<T, N>& b) {
531 return !(a < b);
532 }
533
534 // ========================================
535 // Implementation
536
537 template <typename T, int N>
InlinedVector()538 inline InlinedVector<T, N>::InlinedVector() {
539 InitRep();
540 }
541
542 template <typename T, int N>
InlinedVector(size_t n)543 inline InlinedVector<T, N>::InlinedVector(size_t n) {
544 InitRep();
545 if (n > capacity()) {
546 Grow<Nop>(n); // Must use Nop in case T is not copyable
547 }
548 set_size_internal(n);
549 ValueInit(nullptr, n, data());
550 }
551
552 template <typename T, int N>
InlinedVector(size_t n,const value_type & elem)553 inline InlinedVector<T, N>::InlinedVector(size_t n, const value_type& elem) {
554 InitRep();
555 if (n > capacity()) {
556 Grow<Nop>(n); // Can use Nop since we know we have nothing to copy
557 }
558 set_size_internal(n);
559 Fill(&elem, n, data());
560 }
561
562 template <typename T, int N>
InlinedVector(const InlinedVector & v)563 inline InlinedVector<T, N>::InlinedVector(const InlinedVector& v) {
564 InitRep();
565 *this = v;
566 }
567
568 template <typename T, int N>
insert(iterator pos,const value_type & v)569 typename InlinedVector<T, N>::iterator InlinedVector<T, N>::insert(
570 iterator pos, const value_type& v) {
571 DCHECK_GE(pos, begin());
572 DCHECK_LE(pos, end());
573 if (pos == end()) {
574 push_back(v);
575 return end() - 1;
576 }
577 size_t s = size();
578 size_t idx = std::distance(begin(), pos);
579 if (s == capacity()) {
580 Grow<Move>(s + 1);
581 }
582 CHECK_LT(s, capacity());
583 pos = begin() + idx; // Reset 'pos' into a post-enlarge iterator.
584 Fill(data() + s - 1, 1, data() + s); // data[s] = data[s-1]
585 std::copy_backward(pos, data() + s - 1, data() + s);
586 *pos = v;
587
588 set_size_internal(s + 1);
589 return pos;
590 }
591
592 template <typename T, int N>
erase(iterator first,iterator last)593 typename InlinedVector<T, N>::iterator InlinedVector<T, N>::erase(
594 iterator first, iterator last) {
595 DCHECK_LE(begin(), first);
596 DCHECK_LE(first, last);
597 DCHECK_LE(last, end());
598
599 size_t s = size();
600 ptrdiff_t erase_gap = std::distance(first, last);
601 std::copy(last, data() + s, first);
602 Destroy(data() + s - erase_gap, erase_gap);
603 set_size_internal(s - erase_gap);
604 return first;
605 }
606
607 template <typename T, int N>
swap(InlinedVector & other)608 void InlinedVector<T, N>::swap(InlinedVector& other) {
609 using std::swap; // Augment ADL with std::swap.
610 if (&other == this) {
611 return;
612 }
613
614 InlinedVector* a = this;
615 InlinedVector* b = &other;
616
617 const bool a_inline = a->is_inline();
618 const bool b_inline = b->is_inline();
619
620 if (!a_inline && !b_inline) {
621 // Just swap the top-level representations.
622 T* aptr = a->outofline_pointer();
623 T* bptr = b->outofline_pointer();
624 a->set_outofline_pointer(bptr);
625 b->set_outofline_pointer(aptr);
626
627 uint64_t aword = a->outofline_word();
628 uint64_t bword = b->outofline_word();
629 a->set_outofline_word(bword);
630 b->set_outofline_word(aword);
631 return;
632 }
633
634 // Make a the larger of the two to reduce number of cases.
635 size_t a_size = a->size();
636 size_t b_size = b->size();
637 if (a->size() < b->size()) {
638 swap(a, b);
639 swap(a_size, b_size);
640 }
641 DCHECK_GE(a_size, b_size);
642
643 if (b->capacity() < a_size) {
644 b->Grow<Move>(a_size);
645 }
646
647 // One is inline and one is not.
648 // 'a' is larger. Swap the elements up to the smaller array size.
649 std::swap_ranges(a->data(), a->data() + b_size, b->data());
650 std::uninitialized_copy(a->data() + b_size, a->data() + a_size,
651 b->data() + b_size);
652 Destroy(a->data() + b_size, a_size - b_size);
653 a->set_size_internal(b_size);
654 b->set_size_internal(a_size);
655 DCHECK_EQ(b->size(), a_size);
656 DCHECK_EQ(a->size(), b_size);
657 }
658
659 template <typename T, int N>
660 template <typename Iter>
AppendRange(Iter first,Iter last,std::input_iterator_tag)661 inline void InlinedVector<T, N>::AppendRange(Iter first, Iter last,
662 std::input_iterator_tag) {
663 std::copy(first, last, std::back_inserter(*this));
664 }
665
666 template <typename T, int N>
667 template <typename Iter>
AppendRange(Iter first,Iter last,std::forward_iterator_tag)668 inline void InlinedVector<T, N>::AppendRange(Iter first, Iter last,
669 std::forward_iterator_tag) {
670 typedef typename std::iterator_traits<Iter>::difference_type Length;
671 Length length = std::distance(first, last);
672 size_t s = size();
673 reserve(s + length);
674 std::uninitialized_copy_n(first, length, data() + s);
675 set_size_internal(s + length);
676 }
677
678 template <typename T, int N>
679 template <typename Iter>
AppendRange(Iter first,Iter last)680 inline void InlinedVector<T, N>::AppendRange(Iter first, Iter last) {
681 typedef typename std::iterator_traits<Iter>::iterator_category IterTag;
682 AppendRange(first, last, IterTag());
683 }
684
685 } // namespace gtl
686 } // namespace tensorflow
687
688 #endif // TENSORFLOW_LIB_GTL_INLINED_VECTOR_H_
689