Index: base/containers/vector_buffer.h |
diff --git a/base/containers/vector_buffer.h b/base/containers/vector_buffer.h |
new file mode 100644 |
index 0000000000000000000000000000000000000000..c24c91b9cb4f8647ba8b4bd6a56e6fcdf8b43cab |
--- /dev/null |
+++ b/base/containers/vector_buffer.h |
@@ -0,0 +1,152 @@ |
+// Copyright 2017 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#ifndef BASE_CONTAINERS_VECTOR_BUFFERS_H_ |
+#define BASE_CONTAINERS_VECTOR_BUFFERS_H_ |
+ |
+#include <stdlib.h> |
+#include <string.h> |
+ |
+#include <type_traits> |
+#include <utility> |
+ |
+#include "base/logging.h" |
+#include "base/macros.h" |
+ |
+namespace base { |
+namespace internal { |
+ |
+// Internal implementation detail of base/containers. |
+// |
+// Implements a vector-like buffer that holds a certain capacity of T. Unlike |
+// std::vector, VectorBuffer never constructs or destructs its arguments, and |
+// can't change sizes. But it does implement templates to assist in efficient |
+// moving and destruction of those items manually. |
+// |
+// In particular, te destructor function does not iterate over the items if |
+// there is no destructor. Moves should be implemented as a memcpy/memmove for |
+// trivially copyable objects (POD) otherwise, it should be a std::move if |
+// possible, and as a last resort it falls back to a copy. This behavior is |
+// similar to std::vector. |
+// |
+// No special consideration is done for noexcept move constructors since |
+// we compile without exceptions. |
+// |
+// The current API does not support moving overlapping ranges. |
+template <typename T> |
+class VectorBuffer { |
+ public: |
+ VectorBuffer() {} |
+ VectorBuffer(size_t count) |
+ : buffer_(reinterpret_cast<T*>(malloc(sizeof(T) * count))), |
+ capacity_(count) {} |
+ VectorBuffer(VectorBuffer&& other) noexcept |
+ : buffer_(other.buffer_), capacity_(other.capacity_) { |
+ other.buffer_ = nullptr; |
+ other.capacity_ = 0; |
+ } |
+ |
+ ~VectorBuffer() { free(buffer_); } |
+ |
+ VectorBuffer& operator=(VectorBuffer&& other) { |
+ free(buffer_); |
+ buffer_ = other.buffer_; |
+ capacity_ = other.capacity_; |
+ |
+ other.buffer_ = nullptr; |
+ other.capacity_ = 0; |
+ return *this; |
+ } |
+ |
+ size_t capacity() const { return capacity_; } |
+ |
+ T& operator[](size_t i) { return buffer_[i]; } |
+ const T& operator[](size_t i) const { return buffer_[i]; } |
+ T* begin() { return buffer_; } |
+ T* end() { return &buffer_[capacity_]; } |
+ |
+ // DestructRange ------------------------------------------------------------ |
+ |
+ // Trivially destructible objects need not have their destructors called. |
+ template <typename T2 = T, |
+ typename std::enable_if<std::is_trivially_destructible<T2>::value, |
+ int>::type = 0> |
+ void DestructRange(T* begin, T* end) {} |
+ |
+ // Non-trivially destructible objects must have their destructors called |
+ // individually. |
+ template <typename T2 = T, |
+ typename std::enable_if<!std::is_trivially_destructible<T2>::value, |
+ int>::type = 0> |
+ void DestructRange(T* begin, T* end) { |
+ while (begin != end) { |
+ begin->~T(); |
+ begin++; |
+ } |
+ } |
+ |
+ // MoveRange ---------------------------------------------------------------- |
+ // |
+ // The destructor will be called (as necessary) for all moved types. The |
+ // ranges must not overlap. |
+ |
+ // Trivially copyable types can use memcpy. trivially copyable implies |
+ // that there is a trivial destructor as we don't have to call it. |
+ template <typename T2 = T, |
+ typename std::enable_if<base::is_trivially_copyable<T2>::value, |
+ int>::type = 0> |
+ static void MoveRange(T* from_begin, T* from_end, T* to) { |
+ DCHECK(!RangesOverlap(from_begin, from_end, to)); |
+ memcpy(to, from_begin, (from_end - from_begin) * sizeof(T)); |
+ } |
+ |
+ // Not trivially copyable, but movable: call the move constructor and |
+ // destruct the original. |
+ template <typename T2 = T, |
+ typename std::enable_if<std::is_move_constructible<T2>::value && |
+ !base::is_trivially_copyable<T2>::value, |
+ int>::type = 0> |
+ static void MoveRange(T* from_begin, T* from_end, T* to) { |
+ DCHECK(!RangesOverlap(from_begin, from_end, to)); |
+ while (from_begin != from_end) { |
+ new (to) T(std::move(*from_begin)); |
+ from_begin->~T(); |
+ from_begin++; |
+ to++; |
+ } |
+ } |
+ |
+ // Not movable, not trivially copyable: call the copy constructor and |
+ // destruct the original. |
+ template <typename T2 = T, |
+ typename std::enable_if<!std::is_move_constructible<T2>::value && |
+ !base::is_trivially_copyable<T2>::value, |
+ int>::type = 0> |
+ static void MoveRange(T* from_begin, T* from_end, T* to) { |
+ DCHECK(!RangesOverlap(from_begin, from_end, to)); |
+ while (from_begin != from_end) { |
+ new (to) T(*from_begin); |
+ from_begin->~T(); |
+ from_begin++; |
+ to++; |
+ } |
+ } |
+ |
+ private: |
+ static bool RangesOverlap(const T* from_begin, |
+ const T* from_end, |
+ const T* to) { |
+ return !(to >= from_end || to + (from_end - from_begin) <= from_begin); |
+ } |
+ |
+ T* buffer_ = nullptr; |
+ size_t capacity_ = 0; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(VectorBuffer); |
+}; |
+ |
+} // namespace internal |
+} // namespace base |
+ |
+#endif // BASE_CONTAINERS_VECTOR_BUFFERS_H_ |