// 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 #include #include #include #include "base/logging.h" #include "base/macros.h" #include "base/numerics/checked_math.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 class VectorBuffer { public: constexpr VectorBuffer() = default; #if defined(__clang__) && !defined(__native_client__) // 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( malloc(CheckMul(sizeof(T), count).ValueOrDie()))), 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) { // TODO(crbug.com/817982): Some call sites (at least circular_deque.h) are // calling this with `i == capacity_` as a way of getting `end()`. Therefore // we have to allow this for now (`i <= capacity_`), until we fix those call // sites to use real iterators. This comment applies here and to `const T& // operator[]`, below. CHECK_LE(i, capacity_); return buffer_[i]; } const T& operator[](size_t i) const { CHECK_LE(i, capacity_); return buffer_[i]; } T* begin() { return buffer_; } T* end() { return &buffer_[capacity_]; } // DestructRange ------------------------------------------------------------ // Trivially destructible objects need not have their destructors called. template ::value, int>::type = 0> void DestructRange(T* begin, T* end) {} // Non-trivially destructible objects must have their destructors called // individually. template ::value, int>::type = 0> void DestructRange(T* begin, T* end) { CHECK_LE(begin, 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 ::value, int>::type = 0> static void MoveRange(T* from_begin, T* from_end, T* to) { CHECK(!RangesOverlap(from_begin, from_end, to)); memcpy( to, from_begin, CheckSub(get_uintptr(from_end), get_uintptr(from_begin)).ValueOrDie()); } // Not trivially copyable, but movable: call the move constructor and // destruct the original. template ::value && !base::is_trivially_copyable::value, int>::type = 0> static void MoveRange(T* from_begin, T* from_end, T* to) { CHECK(!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 ::value && !base::is_trivially_copyable::value, int>::type = 0> static void MoveRange(T* from_begin, T* from_end, T* to) { CHECK(!RangesOverlap(from_begin, from_end, to)); while (from_begin != from_end) { new (to) T(*from_begin); from_begin->~T(); from_begin++; to++; } } private: // TODO(crbug.com/817982): What we really need is for checked_math.h to be // able to do checked arithmetic on pointers. static inline uintptr_t get_uintptr(const T* t) { return reinterpret_cast(t); } static bool RangesOverlap(const T* from_begin, const T* from_end, const T* to) { const auto from_begin_uintptr = get_uintptr(from_begin); const auto from_end_uintptr = get_uintptr(from_end); const auto to_uintptr = get_uintptr(to); return !( to >= from_end || CheckAdd(to_uintptr, CheckSub(from_end_uintptr, from_begin_uintptr)) .ValueOrDie() <= from_begin_uintptr); } T* buffer_ = nullptr; size_t capacity_ = 0; DISALLOW_COPY_AND_ASSIGN(VectorBuffer); }; } // namespace internal } // namespace base #endif // BASE_CONTAINERS_VECTOR_BUFFERS_H_