/* * * Copyright 2017 gRPC authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ #ifndef GRPC_CORE_LIB_GPRPP_INLINED_VECTOR_H #define GRPC_CORE_LIB_GPRPP_INLINED_VECTOR_H #include #include #include #include "src/core/lib/gprpp/memory.h" namespace grpc_core { // NOTE: We eventually want to use absl::InlinedVector here. However, // there are currently build problems that prevent us from using absl. // In the interim, we define a custom implementation as a place-holder, // with the intent to eventually replace this with the absl // implementation. // // This place-holder implementation does not implement the full set of // functionality from the absl version; it has just the methods that we // currently happen to need in gRPC. If additional functionality is // needed before this gets replaced with the absl version, it can be // added, with the following proviso: // // ANY METHOD ADDED HERE MUST COMPLY WITH THE INTERFACE IN THE absl // IMPLEMENTATION! // // TODO(nnoble, roth): Replace this with absl::InlinedVector once we // integrate absl into the gRPC build system in a usable way. template class InlinedVector { public: InlinedVector() { init_data(); } ~InlinedVector() { destroy_elements(); } // copy constructor InlinedVector(const InlinedVector& v) { init_data(); copy_from(v); } InlinedVector& operator=(const InlinedVector& v) { if (this != &v) { clear(); copy_from(v); } return *this; } // move constructor InlinedVector(InlinedVector&& v) { init_data(); move_from(v); } InlinedVector& operator=(InlinedVector&& v) { if (this != &v) { clear(); move_from(v); } return *this; } T* data() { return dynamic_ != nullptr ? dynamic_ : reinterpret_cast(inline_); } const T* data() const { return dynamic_ != nullptr ? dynamic_ : reinterpret_cast(inline_); } T& operator[](size_t offset) { assert(offset < size_); return data()[offset]; } const T& operator[](size_t offset) const { assert(offset < size_); return data()[offset]; } bool operator==(const InlinedVector& other) const { if (size_ != other.size_) return false; for (size_t i = 0; i < size_; ++i) { // Note that this uses == instead of != so that the data class doesn't // have to implement !=. if (!(data()[i] == other.data()[i])) return false; } return true; } bool operator!=(const InlinedVector& other) const { return !(*this == other); } void reserve(size_t capacity) { if (capacity > capacity_) { T* new_dynamic = std::alignment_of::value == 0 ? static_cast(gpr_malloc(sizeof(T) * capacity)) : static_cast(gpr_malloc_aligned( sizeof(T) * capacity, std::alignment_of::value)); move_elements(data(), new_dynamic, size_); free_dynamic(); dynamic_ = new_dynamic; capacity_ = capacity; } } void resize(size_t new_size) { while (new_size > size_) emplace_back(); while (new_size < size_) pop_back(); } template void emplace_back(Args&&... args) { if (size_ == capacity_) { reserve(capacity_ * 2); } new (&(data()[size_])) T(std::forward(args)...); ++size_; } void push_back(const T& value) { emplace_back(value); } void push_back(T&& value) { emplace_back(std::move(value)); } void pop_back() { assert(!empty()); size_t s = size(); T& value = data()[s - 1]; value.~T(); size_--; } size_t size() const { return size_; } bool empty() const { return size_ == 0; } size_t capacity() const { return capacity_; } void clear() { destroy_elements(); init_data(); } private: void copy_from(const InlinedVector& v) { // if v is allocated, make sure we have enough capacity. if (v.dynamic_ != nullptr) { reserve(v.capacity_); } // copy over elements for (size_t i = 0; i < v.size_; ++i) { new (&(data()[i])) T(v[i]); } // copy over metadata size_ = v.size_; capacity_ = v.capacity_; } void move_from(InlinedVector& v) { // if v is allocated, then we steal its dynamic array; otherwise, we // move the elements individually. if (v.dynamic_ != nullptr) { dynamic_ = v.dynamic_; } else { move_elements(v.data(), data(), v.size_); } // copy over metadata size_ = v.size_; capacity_ = v.capacity_; // null out the original v.init_data(); } static void move_elements(T* src, T* dst, size_t num_elements) { for (size_t i = 0; i < num_elements; ++i) { new (&dst[i]) T(std::move(src[i])); src[i].~T(); } } void init_data() { dynamic_ = nullptr; size_ = 0; capacity_ = N; } void destroy_elements() { for (size_t i = 0; i < size_; ++i) { T& value = data()[i]; value.~T(); } free_dynamic(); } void free_dynamic() { if (dynamic_ != nullptr) { if (std::alignment_of::value == 0) { gpr_free(dynamic_); } else { gpr_free_aligned(dynamic_); } } } typename std::aligned_storage::type inline_[N]; T* dynamic_; size_t size_; size_t capacity_; }; } // namespace grpc_core #endif /* GRPC_CORE_LIB_GPRPP_INLINED_VECTOR_H */