| // 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" |
| #include "base/numerics/checked_math.h" |
| #include "starboard/memory.h" |
| #include "starboard/types.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; |
| |
| // Cobalt Clang 3.6 compiler does not support no_sanitize. |
| #if defined(__clang__) && !defined(__native_client__) && (!defined(STARBOARD) || (__clang_major__ > 3)) |
| // 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*>( |
| SbMemoryAllocate(CheckMul(sizeof(T), count).ValueOrDie()))), |
| capacity_(count) { |
| } |
| VectorBuffer(VectorBuffer&& other) noexcept |
| : buffer_(other.buffer_), capacity_(other.capacity_) { |
| other.buffer_ = nullptr; |
| other.capacity_ = 0; |
| } |
| |
| ~VectorBuffer() { SbMemoryDeallocate(buffer_); } |
| |
| VectorBuffer& operator=(VectorBuffer&& other) { |
| SbMemoryDeallocate(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 <typename T2 = T, |
| typename std::enable_if<std::is_trivially_destructible<T2>::value, |
| int>::type = 0> |
| void DestructRange(T*, T*) {} |
| |
| // 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) { |
| 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 <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) { |
| CHECK(!RangesOverlap(from_begin, from_end, to)); |
| SbMemoryCopy( |
| 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 <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) { |
| 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 <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) { |
| 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<uintptr_t>(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_ |