slint/api/sixtyfps-cpp/include/sixtyfps_sharedarray.h
Olivier Goffart 4da5f5eee6 C++: Make a model based on a SharedArray like in C++
And make the SharedArray editable
2020-09-15 15:01:33 +02:00

145 lines
4.1 KiB
C++

/* LICENSE BEGIN
This file is part of the SixtyFPS Project -- https://sixtyfps.io
Copyright (c) 2020 Olivier Goffart <olivier.goffart@sixtyfps.io>
Copyright (c) 2020 Simon Hausmann <simon.hausmann@sixtyfps.io>
SPDX-License-Identifier: GPL-3.0-only
This file is also available under commercial licensing terms.
Please contact info@sixtyfps.io for more information.
LICENSE END */
#pragma once
#include "sixtyfps_sharedarray_internal.h"
#include <atomic>
namespace sixtyfps {
template<typename T>
struct SharedArray
{
SharedArray()
: inner(const_cast<SharedArrayHeader*>(reinterpret_cast<const SharedArrayHeader*>(
cbindgen_private::sixtyfps_shared_array_empty())))
{ }
SharedArray(const SharedArray &other)
: inner(other.inner)
{
if (inner->refcount > 0) {
++inner->refcount;
}
}
~SharedArray()
{
drop();
}
SharedArray &operator=(const SharedArray &other)
{
if (other.inner == inner) { return *this; }
drop();
inner = other.inner;
if (inner->refcount > 0) {
++inner->refcount;
}
return *this;
}
SharedArray &operator=(SharedArray &&other)
{
std::swap(inner, other.inner);
return *this;
}
const T *cbegin() const {
return reinterpret_cast<const T *>(inner + 1);
}
const T *cend() const {
return cbegin() + inner->size;
}
const T *begin() const { return cbegin(); }
const T *end() const { return cend(); }
T *begin() {
detach(inner->size);
return reinterpret_cast<T *>(inner + 1);
}
T *end() {
detach(inner->size);
return begin() + inner->size;
}
std::size_t size() const {
return inner->size;
}
T &operator[](std::size_t index) {
return begin()[index];
}
const T &operator[](std::size_t index) const {
return begin()[index];
}
const T &at(std::size_t index) const {
return begin()[index];
}
void push_back(const T &value) {
detach(inner->size + 1);
new (end()) T(value);
inner->size++;
}
void push_back(T &&value) {
detach(inner->size + 1);
new (end()) T(std::move(value));
inner->size++;
}
private:
void detach(std::size_t expected_capacity) {
if (inner->refcount == 1 && expected_capacity <= inner->capacity) {
return;
}
auto new_array = SharedArray::with_capacity(expected_capacity);
auto old_data = reinterpret_cast<const T *>(inner + 1);
auto new_data = reinterpret_cast<T *>(new_array.inner + 1);
for (std::size_t i = 0; i < inner->size; ++i) {
new (new_data+i) T(old_data[i]);
new_array.inner->size++;
}
*this = std::move(new_array);
}
void drop() {
if (inner->refcount > 0 && (--inner->refcount) == 0) {
auto b = begin(), e = end();
for (auto it = b; it < e; ++it) {
it->~T();
}
cbindgen_private::sixtyfps_shared_array_free(
reinterpret_cast<uint8_t *>(inner),
sizeof(SharedArrayHeader) + inner->capacity * sizeof(T),
alignof(SharedArrayHeader));
}
}
static SharedArray with_capacity(std::size_t capacity) {
auto mem = cbindgen_private::sixtyfps_shared_array_allocate(
sizeof(SharedArrayHeader) + capacity * sizeof(T),
alignof(SharedArrayHeader));
return SharedArray(new (mem) SharedArrayHeader{ {1}, 0, capacity });
}
#if !defined(DOXYGEN)
// Unfortunately, this cannot be generated by cbindgen because std::atomic is not understood
struct SharedArrayHeader {
std::atomic<std::intptr_t> refcount;
std::size_t size;
std::size_t capacity;
};
static_assert(alignof(T) <= alignof(SharedArrayHeader), "Not yet supported because we would need to add padding");
SharedArrayHeader *inner;
explicit SharedArray(SharedArrayHeader *inner) : inner(inner) {}
#endif
};
}