blob: 42bf0ae35798c6f355e12a93eb2af32bb21a2783 [file] [log] [blame]
// Copyright 2017 The Fuchsia 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 LIB_FIDL_LLCPP_VECTOR_VIEW_H_
#define LIB_FIDL_LLCPP_VECTOR_VIEW_H_
#include <lib/fidl/llcpp/fidl_allocator.h>
#include <lib/fidl/llcpp/tracking_ptr.h>
#include <lib/fidl/walker.h>
#include <zircon/fidl.h>
#include <iterator>
#include <type_traits>
namespace {
class LayoutChecker;
} // namespace
namespace fidl {
// VectorView is the representation of a FIDL vector in LLCPP.
//
// VectorViews provide limited functionality to access and set fields of the
// vector and other objects like fidl::Array or std::vector must be used to
// construct it.
//
// VectorView's layout and data format must match fidl_vector_t as it will be
// reinterpret_casted into fidl_vector_t during linearization.
//
// Example:
// uint32_t arr[5] = { 1, 2, 3 };
// SomeLLCPPObject obj;
// obj.set_vec_field(VectorView(vv));
template <typename T>
class VectorView {
template <typename>
friend class VectorView;
using marked_count = uint64_t;
// The MSB of count_ stores whether or not data_ is owned by VectorView.
static constexpr marked_count kOwnershipMask = internal::kVectorOwnershipMask;
// The maximum count to avoid colliding with the ownership bit.
static constexpr uint64_t kMaxCount = uint64_t(kOwnershipMask) - 1ULL;
public:
using elem_type = T;
VectorView() {}
VectorView(tracking_ptr<T[]>&& data, uint64_t count) {
set_data_internal(std::move(data));
set_count(count);
}
// Allocates a vector using the allocator.
VectorView(AnyAllocator& allocator, size_t count)
: count_(count), data_(allocator.AllocateVector<T>(count)) {}
// Ideally these constructors wouldn't be needed, but automatic deduction into the tracking_ptr
// doesn't currently work. A deduction guide can fix this, but it is C++17-only.
VectorView(unowned_ptr_t<T> data, uint64_t count) : VectorView(tracking_ptr<T[]>(data), count) {}
template <typename U = T, typename = std::enable_if_t<std::is_const<U>::value>>
VectorView(unowned_ptr_t<std::remove_const_t<U>> data, uint64_t count)
: VectorView(tracking_ptr<T[]>(data), count) {}
VectorView(std::unique_ptr<T[]>&& data, uint64_t count)
: VectorView(tracking_ptr<T[]>(std::move(data)), count) {}
VectorView(std::nullptr_t data, uint64_t count) : VectorView(tracking_ptr<T[]>(data), count) {}
// This constructor triggers a static assertion in tracking_ptr.
template <typename U, typename = std::enable_if_t<!std::is_array<U>::value>>
VectorView(U* data, uint64_t count) : VectorView(tracking_ptr<U[]>(data), count) {}
template <typename U>
VectorView(VectorView<U>&& other) {
static_assert(
std::is_same<T, U>::value || std::is_same<T, std::add_const_t<U>>::value,
"VectorView<T> can only be move-constructed from VectorView<T> or VectorView<const T>");
count_ = other.count_;
data_ = other.data_;
other.release();
}
~VectorView() {
if (is_owned()) {
delete[] data_;
}
}
template <typename U>
VectorView& operator=(VectorView<U>&& other) {
static_assert(std::is_same<T, U>::value || std::is_same<T, std::add_const_t<U>>::value,
"VectorView<T> can only be assigned from VectorView<T> or VectorView<const T>");
if (data_ != nullptr && is_owned()) {
delete[] data_;
}
count_ = other.count_;
data_ = other.data_;
other.release();
return *this;
}
VectorView(const VectorView&) = delete;
VectorView& operator=(const VectorView&) = delete;
uint64_t count() const { return count_ & ~kOwnershipMask; }
void set_count(uint64_t count) {
if (count > kMaxCount) {
abort();
}
count_ = count | (count_ & kOwnershipMask);
}
const T* data() const { return data_; }
void set_data(tracking_ptr<T[]> data) { set_data_internal(std::move(data)); }
T* mutable_data() const { return data_; }
bool empty() const { return count() == 0; }
const T& at(size_t offset) const { return data()[offset]; }
T& at(size_t offset) { return mutable_data()[offset]; }
const T& operator[](size_t offset) const { return at(offset); }
T& operator[](size_t offset) { return at(offset); }
T* begin() { return mutable_data(); }
const T* begin() const { return data(); }
const T* cbegin() const { return data(); }
T* end() { return mutable_data() + count(); }
const T* end() const { return data() + count(); }
const T* cend() const { return data() + count(); }
fidl_vector_t* impl() { return this; }
void Allocate(AnyAllocator& allocator, size_t count) {
if (is_owned()) {
delete[] data_;
}
count_ = count;
data_ = allocator.AllocateVector<T>(count);
}
private:
void set_data_internal(tracking_ptr<T[]>&& data) {
if (data_ != nullptr && is_owned()) {
delete[] data_;
}
if (data.is_owned()) {
count_ |= kOwnershipMask;
} else {
count_ &= ~kOwnershipMask;
}
data_ = data.get();
data.release();
}
bool is_owned() const noexcept { return count_ & kOwnershipMask; }
void release() {
if (is_owned()) {
// Match unique_ptr std::move behavior in owned and unowned case
// (unowned case doesn't reset source).
count_ = 0;
data_ = nullptr;
}
}
friend ::LayoutChecker;
// The lower 63 bits of count_ are reserved to store the number of elements.
// The MSB stores ownership of the data_ pointer.
marked_count count_ = 0;
T* data_ = nullptr;
};
} // namespace fidl
namespace {
class LayoutChecker {
static_assert(sizeof(fidl::VectorView<uint8_t>) == sizeof(fidl_vector_t),
"VectorView size should match fidl_vector_t");
static_assert(offsetof(fidl::VectorView<uint8_t>, count_) == offsetof(fidl_vector_t, count),
"VectorView count offset should match fidl_vector_t");
static_assert(sizeof(fidl::VectorView<uint8_t>::count_) == sizeof(fidl_vector_t::count),
"VectorView count size should match fidl_vector_t");
static_assert(offsetof(fidl::VectorView<uint8_t>, data_) == offsetof(fidl_vector_t, data),
"VectorView data offset should match fidl_vector_t");
static_assert(sizeof(fidl::VectorView<uint8_t>::data_) == sizeof(fidl_vector_t::data),
"VectorView data size should match fidl_vector_t");
};
} // namespace
#endif // LIB_FIDL_LLCPP_VECTOR_VIEW_H_