/* LICENSE BEGIN This file is part of the SixtyFPS Project -- https://sixtyfps.io Copyright (c) 2020 Olivier Goffart Copyright (c) 2020 Simon Hausmann 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 namespace sixtyfps { template struct SharedArray { SharedArray() : inner(const_cast(reinterpret_cast( 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(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(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(inner + 1); auto new_data = reinterpret_cast(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(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 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 }; }