|  | // 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" | 
|  |  | 
|  | 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, the 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: | 
|  | constexpr VectorBuffer() = default; | 
|  |  | 
|  | #if defined(__clang__) | 
|  | // This constructor converts an uninitialized void* to a T* which triggers | 
|  | // clang Control Flow Integrity. Since this is as-designed, disable. | 
|  | __attribute__((no_sanitize("cfi-unrelated-cast", "vptr"))) | 
|  | #endif | 
|  | 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. | 
|  | // | 
|  | // The parameters and begin and end (one past the last) of the input buffer, | 
|  | // and the address of the first element to copy to. There must be sufficient | 
|  | // room in the destination for all items in the range [begin, end). | 
|  |  | 
|  | // 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<std::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 && | 
|  | !std::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 && | 
|  | !std::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; | 
|  |  | 
|  | VectorBuffer(const VectorBuffer&) = delete; | 
|  | VectorBuffer& operator=(const VectorBuffer&) = delete; | 
|  | }; | 
|  |  | 
|  | }  // namespace internal | 
|  | }  // namespace base | 
|  |  | 
|  | #endif  // BASE_CONTAINERS_VECTOR_BUFFERS_H_ |