diff options
Diffstat (limited to 'include')
-rw-r--r-- | include/fud_algorithm.hpp | 36 | ||||
-rw-r--r-- | include/fud_array.hpp | 10 | ||||
-rw-r--r-- | include/fud_memory.hpp | 16 | ||||
-rw-r--r-- | include/fud_option.hpp | 229 | ||||
-rw-r--r-- | include/fud_span.hpp | 10 | ||||
-rw-r--r-- | include/fud_status.hpp | 66 | ||||
-rw-r--r-- | include/fud_vector.hpp | 628 |
7 files changed, 939 insertions, 56 deletions
diff --git a/include/fud_algorithm.hpp b/include/fud_algorithm.hpp index e3d5d3b..0ad71d5 100644 --- a/include/fud_algorithm.hpp +++ b/include/fud_algorithm.hpp @@ -18,11 +18,11 @@ #ifndef FUD_ALGORITHM_HPP #define FUD_ALGORITHM_HPP +#include "fud_option.hpp" #include "fud_span.hpp" #include <concepts> #include <limits> -#include <optional> #include <type_traits> namespace fud { @@ -48,30 +48,41 @@ class Iota { { } - constexpr std::optional<T> operator()() noexcept + constexpr Iota(const Iota& rhs) noexcept = default; + + constexpr Iota(Iota&& rhs) noexcept = default; + + ~Iota() noexcept = default; + + Iota& operator=(const Iota& rhs) = default; + + Iota& operator=(Iota&& rhs) = default; + + constexpr Option<T> operator()() noexcept { auto value = m_value; if (m_increment > 0) { if (m_limit - m_increment < m_value) { - return std::nullopt; + return NullOpt; } } else { if (m_limit + m_increment + 1 >= m_value) { - return std::nullopt; + return NullOpt; } } m_value += m_increment; return value; } - void set(T value) { + void set(T value) + { m_value = value; } private: T m_value; - const T m_increment; - const T m_limit; + T m_increment; + T m_limit; }; template <typename T, size_t Size, typename Func> @@ -91,18 +102,19 @@ Span<T, Size> mapTo(Span<T, Size> input, Span<U, Size> output, Func&& mapFunc) output[idx] = std::forward<Func>(mapFunc)(input[idx]); } - return input; + return output; } -template <typename T, size_t Size, typename Func, typename Builder, typename Output> +template <typename T, size_t Size, typename Func, typename Builder> auto map(Span<T, Size> input, Func&& mapFunc, Builder&& builder) -> decltype(std::forward<Builder>(builder)()) { + using Output = decltype(std::forward<Builder>(builder)()); Output output{std::forward<Builder>(builder)()}; for (auto idx = 0; idx < input.size() && idx < output.size(); ++idx) { output[idx] = std::forward<Func>(mapFunc)(input[idx]); } - return input; + return output; } template <typename Generator, typename Builder> @@ -132,7 +144,7 @@ bool allOf(Generator&& generator, Func&& predicate) { bool result = true; while (auto val = std::forward<Generator>(generator)()) { - result = result && std::forward<Func>(predicate)(*val); + result = result && std::forward<Func>(predicate)(val.value()); } return result; } @@ -152,7 +164,7 @@ bool anyOf(Generator&& generator, Func&& predicate) { bool result = false; while (auto val = std::forward<Generator>(generator)()) { - result = result || std::forward<Func>(predicate)(*val); + result = result || std::forward<Func>(predicate)(val.value()); } return result; } diff --git a/include/fud_array.hpp b/include/fud_array.hpp index 807621a..dcbd54a 100644 --- a/include/fud_array.hpp +++ b/include/fud_array.hpp @@ -18,9 +18,10 @@ #ifndef FUD_ARRAY_HPP #define FUD_ARRAY_HPP -#include <cstddef> - #include "fud_memory.hpp" +#include "fud_span.hpp" + +#include <cstddef> namespace fud { @@ -106,6 +107,11 @@ struct Array { constexpr bool operator==(const Array<T, Size>&) const noexcept = default; constexpr auto operator<=>(const Array<T, Size>& other) const noexcept = default; + + Span<T, Size> span() + { + return Span<T, Size>{data(), Size}; + } }; } // namespace fud diff --git a/include/fud_memory.hpp b/include/fud_memory.hpp index 97328a9..6ce6312 100644 --- a/include/fud_memory.hpp +++ b/include/fud_memory.hpp @@ -57,6 +57,22 @@ constexpr void setMemory(Container<T, Size>& container, const T& value) } } +template <template <class, size_t> class Container, typename T, size_t Size> +constexpr void setMemory(Container<T, Size>& container, T&& value) +{ + for (auto& elt : container) { + elt = value; + } +} + +template <template <size_t> class Container, typename T, size_t Size> +constexpr void setMemory(Container<Size>& container, T&& value) +{ + for (auto& elt : container) { + elt = value; + } +} + template <size_t Count, typename T, typename U> void copyMem(T& destination, const U& source) { diff --git a/include/fud_option.hpp b/include/fud_option.hpp new file mode 100644 index 0000000..ca3954f --- /dev/null +++ b/include/fud_option.hpp @@ -0,0 +1,229 @@ +/* + * libfud + * Copyright 2024 Dominick Allen + * + * 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 FUD_OPTION_HPP +#define FUD_OPTION_HPP + +#include "fud_assert.hpp" + +#include <cstddef> +#include <functional> +#include <new> // IWYU pragma: keep (placement new) +#include <type_traits> + +namespace fud { + +namespace option_detail { + +struct NullOptionType { + enum class NullOptConstructor : char + { + Monostate + }; + + constexpr explicit NullOptionType(NullOptConstructor /*unnamed*/) + { + } +}; + +template <size_t Size> +struct DataArray { + // NOLINTBEGIN(cppcoreguidelines-avoid-c-arrays) + std::byte m_data[Size]; + // NOLINTEND(cppcoreguidelines-avoid-c-arrays) + + constexpr std::byte* data() noexcept + { + return m_data; + } + + [[nodiscard]] constexpr const std::byte* data() const noexcept + { + return m_data; + } + + constexpr bool operator==(const DataArray&) const noexcept = default; + + constexpr void clear() + { + for (size_t idx = 0; idx < Size; ++idx) { + m_data[idx] = std::byte(0); + } + } +}; + +} // namespace option_detail + +inline constexpr option_detail::NullOptionType NullOpt{option_detail::NullOptionType::NullOptConstructor::Monostate}; + +template <typename T> +class Option { + private: + static_assert(!std::is_same_v<T, option_detail::NullOptionType>); + static constexpr bool IsRef = std::is_reference_v<T>; + using ValueType = typename std::remove_reference<T>::type; + static constexpr size_t Size = IsRef ? sizeof(std::reference_wrapper<ValueType>) : sizeof(ValueType); + + public: + constexpr Option() noexcept : m_engaged{false} + { + } + + constexpr Option(option_detail::NullOptionType nullOpt) noexcept : m_engaged{false} + { + static_cast<void>(nullOpt); + } + + constexpr Option(T value) noexcept : m_engaged{true} + { + if constexpr (IsRef) { + new (m_data.data()) std::reference_wrapper<ValueType>(std::ref(value)); + if (!m_engaged) { + std::abort(); + } + } else { + new (m_data.data()) ValueType(value); + if (!m_engaged) { + std::abort(); + } + } + } + + constexpr Option(const Option& rhs) noexcept : m_engaged(rhs.m_engaged), m_data(rhs.m_data) + { + } + + constexpr Option(Option&& rhs) noexcept : m_engaged(rhs.m_engaged), m_data(std::move(rhs.m_data)) + { + rhs.cleanup(); + } + + ~Option() noexcept + { + destroy(); + } + + Option& operator=(const Option& rhs) noexcept + { + if (&rhs == this) { + return *this; + } + destroy(); + m_engaged = rhs.m_engaged; + m_data = rhs.m_data; + return *this; + } + + Option& operator=(Option&& rhs) noexcept + { + destroy(); + m_engaged = rhs.m_engaged; + m_data = std::move(rhs.m_data); + rhs.cleanup(); + return *this; + } + + [[nodiscard]] bool hasValue() const + { + return m_engaged; + } + + operator bool() const { + return hasValue(); + } + + [[nodiscard]] constexpr const ValueType& value() const& + { + fudAssert(m_engaged); + if constexpr (IsRef) { + return *reinterpret_cast<const std::reference_wrapper<ValueType>*>(m_data.data()); + } else { + return *reinterpret_cast<const ValueType*>(m_data.data()); + } + } + + [[nodiscard]] constexpr ValueType& value() & + { + fudAssert(m_engaged); + if constexpr (IsRef) { + return *reinterpret_cast<std::reference_wrapper<ValueType>*>(m_data.data()); + } else { + return *reinterpret_cast<ValueType*>(m_data.data()); + } + } + + [[nodiscard]] constexpr const ValueType&& value() const&& + { + fudAssert(m_engaged); + static_assert(!IsRef); + return *reinterpret_cast<const ValueType*>(m_data.data()); + } + + template <typename F> + constexpr auto map(F&& func) const & -> Option<decltype(std::forward<F>(func)(value()))> + { + using U = decltype(std::forward<F>(func)(value())); + // static_assert(std::is_same_v<decltype(std::forward<F>(func)(value())), Option<U>>()); + if (hasValue()) { + return Option<U>{std::forward<F>(func)(value())}; + } + return Option<U>{NullOpt}; + } + + private: + constexpr void destroy() noexcept + { + if (m_engaged) { + if constexpr (IsRef) { + // reinterpret_cast<std::reference_wrapper<ValueType>*>(m_data.data()); + } else { + reinterpret_cast<ValueType*>(m_data.data())->~ValueType(); + } + cleanup(); + } + } + + constexpr void cleanup() noexcept + { + m_engaged = false; + m_data.clear(); + } + + // alignas(maxAlign) Array<uint8_t, maxSize> priv_m_data; + + alignas(alignof(T)) option_detail::DataArray<Size> m_data{}; + + bool m_engaged; +}; + +namespace test { + +void testOption() +{ + Option<int> intOpt; + static_cast<void>(intOpt); + Option<int&> intRefNull; + static_cast<void>(intRefNull); + int value; + Option<int&> intRefValue{value}; +} + +} // namespace test + +} // namespace fud + +#endif diff --git a/include/fud_span.hpp b/include/fud_span.hpp index 5b8497e..ed4bcc7 100644 --- a/include/fud_span.hpp +++ b/include/fud_span.hpp @@ -18,7 +18,6 @@ #ifndef FUD_SPAN_HPP #define FUD_SPAN_HPP -#include "fud_array.hpp" #include "fud_result.hpp" #include "fud_status.hpp" @@ -27,11 +26,17 @@ namespace fud { +template <typename T, size_t Size> +struct Array; + template <typename T, size_t Size = SIZE_MAX> struct Span { static_assert(Size > 0); using ValueType = T; + T* m_data; + const size_t m_size; + static Span make(Array<T, Size>& array) { Span<T, Size> output{array.data(), Size}; @@ -89,9 +94,6 @@ struct Span { return output; } - T* m_data; - const size_t m_size; - [[nodiscard]] constexpr size_t size() const { if constexpr (Size < SIZE_MAX) { diff --git a/include/fud_status.hpp b/include/fud_status.hpp index 91048ac..d57a9c5 100644 --- a/include/fud_status.hpp +++ b/include/fud_status.hpp @@ -23,24 +23,26 @@ namespace fud { enum class [[nodiscard]] FudStatus { Success = 0, + Partial, + Failure, NullPointer, - StringInvalid, - ObjectInvalid, - OperationInvalid, - AllocFailure, - DeallocFailure, ArgumentInvalid, + VariantInvalid, + ObjectInvalid, Utf8Invalid, - Failure, + StringInvalid, + OperationInvalid, + AlreadyInitialized, + FormatInvalid, + RangeError, + IndexInvalid, + Exists, NotFound, - Aliased, Empty, - Partial, Full, - RangeError, - VariantInvalid, - BadArrayLength, - FormatInvalid, + Aliased, + AllocFailure, + DeallocFailure, NotImplemented, NotSupported }; @@ -50,6 +52,10 @@ constexpr const char* FudStatusToString(FudStatus status) switch (status) { case FudStatus::Success: return "Success"; + case FudStatus::Partial: + return "Partial"; + case FudStatus::Failure: + return "Failure"; case FudStatus::NullPointer: return "NullPointer"; case FudStatus::StringInvalid: @@ -58,34 +64,34 @@ constexpr const char* FudStatusToString(FudStatus status) return "ObjectInvalid"; case FudStatus::OperationInvalid: return "OperationInvalid"; - case FudStatus::AllocFailure: - return "AllocFailure"; - case FudStatus::DeallocFailure: - return "DeallocFailure"; case FudStatus::ArgumentInvalid: return "ArgumentInvalid"; case FudStatus::Utf8Invalid: return "Utf8Invalid"; - case FudStatus::Failure: - return "Failure"; - case FudStatus::NotFound: - return "NotFound"; - case FudStatus::Aliased: - return "Aliased"; - case FudStatus::Empty: - return "Empty"; - case FudStatus::Partial: - return "Partial"; - case FudStatus::Full: - return "Full"; case FudStatus::RangeError: return "RangeError"; case FudStatus::VariantInvalid: return "VariantInvalid"; - case FudStatus::BadArrayLength: - return "BadArrayLength"; case FudStatus::FormatInvalid: return "FormatInvalid"; + case FudStatus::AlreadyInitialized: + return "AlreadyInitialized"; + case FudStatus::IndexInvalid: + return "IndexInvalid"; + case FudStatus::Exists: + return "Exists"; + case FudStatus::NotFound: + return "NotFound"; + case FudStatus::Empty: + return "Empty"; + case FudStatus::Full: + return "Full"; + case FudStatus::Aliased: + return "Aliased"; + case FudStatus::AllocFailure: + return "AllocFailure"; + case FudStatus::DeallocFailure: + return "DeallocFailure"; case FudStatus::NotImplemented: return "NotImplemented"; case FudStatus::NotSupported: diff --git a/include/fud_vector.hpp b/include/fud_vector.hpp index 56e1659..f90819a 100644 --- a/include/fud_vector.hpp +++ b/include/fud_vector.hpp @@ -19,42 +19,654 @@ #define FUD_VECTOR_HPP #include "fud_allocator.hpp" +#include "fud_assert.hpp" +#include "fud_config.hpp" +#include "fud_option.hpp" #include "fud_result.hpp" +#include "fud_span.hpp" #include "fud_status.hpp" #include <cstddef> +#include <functional> +#include <new> // IWYU pragma: keep (placement new) namespace fud { template <typename T> class Vector { + static constexpr size_t ElementSize = sizeof(T); + static constexpr size_t Alignment = alignof(T); + public: - static Result<Vector<T>, FudStatus> from(const Vector<T>& rhs); + constexpr Vector() noexcept = default; + constexpr Vector(const Vector<T>& rhs) = delete; + constexpr Vector(Vector<T>&& rhs) noexcept : + m_allocator(rhs.m_allocator), m_data(rhs.m_data), m_length{rhs.m_length}, m_capacity{rhs.m_capacity} + { + rhs.m_allocator = nullptr; + rhs.m_data = nullptr; + rhs.m_length = 0; + rhs.m_capacity = 0; + } + + ~Vector() noexcept + { + static_cast<void>(cleanup()); + } + + Vector& operator=(const Vector<T>& rhs) = delete; + + Vector& operator=(Vector<T>&& rhs) noexcept + { + cleanup(); + m_allocator = rhs.m_allocator; + m_data = rhs.m_data; + m_length = rhs.m_length; + m_capacity = rhs.m_length; + + rhs.m_allocataor = nullptr; + rhs.m_data = nullptr; + rhs.m_length = 0; + rhs.m_capacity = 0; + } + + static Result<Vector<T>, FudStatus> withCapacity(size_t capacity, Allocator* allocator = &globalFudAllocator) + { + Vector<T> output{}; + auto status = initializeWithCapacity(output, capacity, allocator); + if (status != FudStatus::Success) { + return status; + } + return output; + } + + static FudStatus initializeWithCapacity( + Vector<T>& output, + size_t capacity, + Allocator* allocator = &globalFudAllocator) + { + if (output.m_data != nullptr) { + return FudStatus::AlreadyInitialized; + } + + if (allocator == nullptr) { + return FudStatus::NullPointer; + } + + if (capacity > SIZE_MAX / ElementSize) { + return FudStatus::ArgumentInvalid; + } + + size_t requestedSize = capacity * ElementSize; + auto dataPtrResult = allocator->allocate(requestedSize, Alignment); + if (dataPtrResult.isError()) { + return dataPtrResult.getError(); + } + + output.m_allocator = allocator; + output.m_data = static_cast<T*>(dataPtrResult.getOkay()); + output.m_length = 0; + output.m_capacity = capacity; + return FudStatus::Success; + } + + static Result<Vector<T>, FudStatus> withSize(size_t count, Allocator* allocator = &globalFudAllocator) + { + Vector<T> output{}; + auto status = initializeWithCapacity(output, count, allocator); + if (status != FudStatus::Success) { + return status; + } + return output; + } + + static Result<Vector<T>, FudStatus> initializeWithSize( + Vector<T>& output, + size_t count, + Allocator* allocator = &globalFudAllocator) + { + if (output.m_data != nullptr) { + return FudStatus::AlreadyInitialized; + } + + auto status = Vector::initializeWithCapacity(output, count, allocator); + if (status != FudStatus::Success) { + return status; + } + + output.m_length = count; + for (size_t index = 0; index < count; ++index) { + const auto* ptr = new (output.m_data + index) T(); + fudAssert(ptr != nullptr); + } + return output; + } + + template <typename Builder> + static Result<Vector<T>, FudStatus> withSizeFallible( + size_t count, + Builder&& builder, + Allocator* allocator = &globalFudAllocator) + { + Vector<T> output{}; + auto status = initializeWithSizeFallible(output, count, std::forward<Builder>(builder), allocator); + if (status != FudStatus::Success) { + return status; + } + return output; + } + + template <typename Builder> + static Result<Vector<T>, FudStatus> initializeWithSizeFallible( + Vector<T>& output, + size_t count, + Builder&& builder, + Allocator* allocator = &globalFudAllocator) + { + using BuilderResult = decltype(std::forward<Builder>(builder)()); + static_assert(std::is_same_v<BuilderResult, FudStatus>()); + + auto status = Vector::initializeWithCapacity(output, count, allocator); + if (status != FudStatus::Success) { + return status; + } + + output.m_length = count; + + for (size_t index = 0; index < count; ++index) { + auto builderResult{std::forward<Builder>(builder)(output.m_data[index])}; + if (builderResult.isError()) { + return builderResult.takeError(); + } + } + + return output; + } - static Vector<T> move(Vector<T>&& rhs); + static Result<Vector<T>, FudStatus> from(const Vector<T>& rhs, Option<Allocator*> allocatorOption = NullOpt) + { + Allocator* allocator = nullptr; + if (allocatorOption.hasValue()) { + allocator = allocatorOption.value(); + if (allocator == nullptr) { + return FudStatus::NullPointer; + } + } else { + allocator = rhs.allocator; + if (allocator == nullptr) { + return FudStatus::ArgumentInvalid; + } + } + + fudAssert(rhs.m_length <= rhs.m_capacity); + + auto spanResult = rhs.span(); + if (spanResult.isError()) { + return spanResult.takeError(); + } + Vector<T> output{}; + auto status = Vector::initializeFromSpan(output, rhs.m_length, allocator); + if (status != FudStatus::Success) { + return status; + } + return output; + } + + template <size_t Size> + static Result<Vector<T>, FudStatus> from(Span<const T, Size>& rhs, Allocator* allocator) + { + Vector<T> output{}; + auto status = initializeFromSpan(output, rhs, allocator); + if (status != FudStatus::Success) { + return status; + } + return output; + } + + template <size_t Size> + static FudStatus initializeFromSpan(Vector<T>& output, Span<const T, Size>& rhs, Allocator* allocator) + { + auto status = Vector::initializeWithCapacity(output, rhs.size(), allocator); + if (status != FudStatus::Success) { + return status; + } + output.m_length = rhs.m_length; + for (size_t index = 0; index < output.m_length; ++index) { + output.m_data[index] = rhs[index]; + } + } + + static Vector<T> move(Vector<T>&& rhs) noexcept + { + return Vector<T>{std::move(rhs)}; + } FudStatus copy(const Vector<T>& rhs); FudStatus take(Vector<T>&& rhs); - [[nodiscard]] size_t size() const { + [[nodiscard]] size_t size() const + { return m_length; } - [[nodiscard]] size_t capacity() const { + [[nodiscard]] size_t capacity() const + { return m_capacity; } - FudStatus reserve(); + Result<Span<const T>, FudStatus> span() const + { + if (m_data == nullptr) { + return FudStatus::ObjectInvalid; + } + return Span{m_data, m_length}; + } + + Result<Span<T>, FudStatus> span() + { + if (m_data == nullptr) { + return FudStatus::ObjectInvalid; + } + return Span{m_data, m_length}; + } + + Result<Span<const T>, FudStatus> span(size_t count) const + { + if (m_data == nullptr) { + return FudStatus::ObjectInvalid; + } + if (count > m_length) { + return FudStatus::ArgumentInvalid; + } + return Span{m_data, count}; + } + + Result<Span<T>, FudStatus> span(size_t count) + { + if (m_data == nullptr) { + return FudStatus::ObjectInvalid; + } + if (count > m_length) { + return FudStatus::ArgumentInvalid; + } + return Span{m_data, count}; + } + + Result<Span<const T>, FudStatus> span(size_t start, size_t count) const + { + if (m_data == nullptr) { + return FudStatus::ObjectInvalid; + } + if (SIZE_MAX - start < m_length || start + count > m_length) { + return FudStatus::ArgumentInvalid; + } + return Span{m_data + start, count}; + } + + Result<Span<T>, FudStatus> span(size_t start, size_t count) + { + if (m_data == nullptr) { + return FudStatus::ObjectInvalid; + } + if (SIZE_MAX - start < m_length || start + count > m_length) { + return FudStatus::ArgumentInvalid; + } + return Span{m_data + start, count}; + } + + FudStatus reserve(size_t count) + { + if (count <= m_capacity) { + return FudStatus::Success; + } + + if (m_allocator == nullptr) { + return FudStatus::ObjectInvalid; + } + + if (count > SIZE_MAX / ElementSize) { + return FudStatus::ArgumentInvalid; + } + + size_t requestedSize = count * ElementSize; + auto dataPtrResult = m_allocator->allocate(requestedSize, Alignment); + if (dataPtrResult.isError()) { + return dataPtrResult.takeError(); + } + + auto* dataPtr = static_cast<T*>(dataPtrResult.takeOkay()); + for (size_t index = 0; index < m_length; ++index) { + const auto* ptr = new (dataPtr + index) T(std::move(m_data[index])); + fudAssert(ptr != nullptr); + m_data[index].~T(); + } + + m_data = dataPtr; + m_capacity = count; + + return FudStatus::Success; + } + + FudStatus resize(size_t count) + { + if (count == m_length) { + return FudStatus::Success; + } + + if (m_allocator == nullptr) { + return FudStatus::ObjectInvalid; + } - FudStatus resize(); + if (count < m_length) { + for (size_t index = count; index < m_length; ++index) { + m_data[index].~T(); + } + m_length = count; + return FudStatus::Success; + } - FudStatus clear(); + auto reserveStatus = reserve(count); + if (reserveStatus != FudStatus::Success) { + return reserveStatus; + } - // FudResult at(); + for (size_t index = m_length; index < count; ++index) { + const auto* ptr = new (m_data + index) T(); + fudAssert(ptr != nullptr); + } + + m_length = count; + return FudStatus::Success; + } + + FudStatus clear() + { + if (m_allocator == nullptr || m_data == nullptr) { + return FudStatus::ObjectInvalid; + } + for (size_t index = 0; index < m_length; ++index) { + m_data[index].~T(); + } + m_length = 0; + return FudStatus::Success; + } + + Result<std::reference_wrapper<T>, FudStatus> get(size_t index) + { + if (m_data == nullptr) { + return FudStatus::ObjectInvalid; + } + if (index >= m_length) { + return FudStatus::IndexInvalid; + } + return std::ref(m_data[index]); + } + + Result<const std::reference_wrapper<const T>, FudStatus> ref(size_t index) const + { + if (m_data == nullptr) { + return FudStatus::ObjectInvalid; + } + if (index >= m_length) { + return FudStatus::IndexInvalid; + } + return std::cref(m_data[index]); + } + + constexpr Option<T&> front() + { + if (m_length > 0) { + fudAssert(m_data != nullptr); + return m_data[0]; + } + return NullOpt; + } + + constexpr Option<const T&> front() const + { + if (m_length > 0) { + fudAssert(m_data != nullptr); + return m_data[0]; + } + return NullOpt; + } + + constexpr Option<T&> back() + { + if (m_length > 0) { + fudAssert(m_data != nullptr); + return m_data[m_length - 1]; + } + return NullOpt; + } + + constexpr Option<const T&> back() const + { + if (m_length > 0) { + return m_data[m_length - 1]; + } + return NullOpt; + } + + constexpr T* data() noexcept + { + return m_data; + } + + constexpr const T* data() const noexcept + { + return m_data; + } + + constexpr T* begin() noexcept + { + return m_data; + } + + constexpr const T* begin() const noexcept + { + return m_data; + } + + constexpr T* end() noexcept + { + return m_data + m_length; + } + + constexpr const T* end() const noexcept + { + return m_data + m_length; + } + + constexpr T& operator[](size_t index) + { + if constexpr (fudBoundsChecking) { + fudAssert(m_data != nullptr); + fudAssert(index < m_length); + } + return m_data[index]; + } + + constexpr const T& operator[](size_t index) const + { + if constexpr (fudBoundsChecking) { + fudAssert(m_data != nullptr); + fudAssert(index < m_length); + } + return m_data[index]; + } + + FudStatus pushBack(const T& value) + { + if (m_length == m_capacity) { + auto status = grow(); + if (status != FudStatus::Success) { + return status; + } + } + const auto* ptr = new (m_data + m_length) T(value); + fudAssert(ptr != nullptr); + m_length++; + return FudStatus::Success; + } + + FudStatus pushBack(T&& value) + { + if (m_length == m_capacity) { + auto status = grow(); + if (status != FudStatus::Success) { + return status; + } + } + const auto* ptr = new (m_data + m_length) T(std::move(value)); + fudAssert(ptr != nullptr); + m_length++; + return FudStatus::Success; + } + + Result<T, FudStatus> popBack() + { + if (m_data == nullptr) { + return FudStatus::ObjectInvalid; + } + if (m_length == 0) { + return FudStatus::Empty; + } + auto result{std::move(m_data[m_length - 1])}; + m_length--; + m_data[m_length].~T(); + return result; + } + + FudStatus eraseBack() + { + if (m_data == nullptr) { + return FudStatus::ObjectInvalid; + } + if (m_length == 0) { + return FudStatus::Empty; + } + m_length--; + m_data[m_length].~T(); + return FudStatus::Success; + } + + FudStatus insert(size_t index, const T& value) + { + if (index > m_length) { + return FudStatus::IndexInvalid; + } + if (index == m_length) { + return pushBack(value); + } + if (m_length == m_capacity) { + auto status = grow(); + if (status != FudStatus::Success) { + return status; + } + } + + const auto* ptr = new (m_data + m_length) T(std::move(m_data[m_length - 1])); + fudAssert(ptr != nullptr); + + for (size_t backIndex = m_length - 1; backIndex > index; --backIndex) { + m_data[backIndex] = std::move(m_data[backIndex - 1]); + } + m_data[index] = value; + m_length++; + return FudStatus::Success; + } + + FudStatus insert(size_t index, T&& value) + { + if (index > m_length) { + return FudStatus::IndexInvalid; + } + if (index == m_length) { + return pushBack(std::move(value)); + } + if (m_length == m_capacity) { + auto status = grow(); + if (status != FudStatus::Success) { + return status; + } + } + + const auto* ptr = new (m_data + m_length) T(std::move(m_data[m_length - 1])); + fudAssert(ptr != nullptr); + + for (size_t backIndex = m_length - 1; backIndex > index; --backIndex) { + m_data[backIndex] = std::move(m_data[backIndex - 1]); + } + m_data[index] = std::move(value); + m_length++; + return FudStatus::Success; + } + + FudStatus erase(size_t index) + { + if (index >= m_length) { + return FudStatus::IndexInvalid; + } + + m_data[index].~T(); + for (size_t fwdIndex = index; fwdIndex + 1 < m_length; fwdIndex++) + { + m_data[fwdIndex] = std::move(m_data[fwdIndex + 1]); + } + m_data[m_length - 1].~T(); + m_length--; + return FudStatus::Success; + } private: + FudStatus grow() + { + // See https://github.com/facebook/folly/blob/main/folly/docs/FBVector.md + size_t additional = m_capacity < 2 ? 1 : m_capacity / 2; + if (SIZE_MAX - additional * ElementSize < m_capacity * ElementSize) { + additional = SIZE_MAX - m_capacity * ElementSize / 2; + } + while (additional > 0) { + auto reserveStatus = reserve(additional + m_capacity); + if (reserveStatus == FudStatus::Success) { + break; + } + if (reserveStatus == FudStatus::AllocFailure) { + additional /= 2; + } else { + return reserveStatus; + } + } + + if (m_length == m_capacity) { + return FudStatus::AllocFailure; + } + + return FudStatus::Success; + } + + FudStatus cleanup() noexcept + { + auto status = clear(); + + if (m_data != nullptr && m_allocator != nullptr) { + auto deallocStatus = m_allocator->deallocate(m_data, m_capacity); + if (status == FudStatus::Success) { + status = deallocStatus; + } + } + + m_allocator = nullptr; + m_data = nullptr; + m_length = 0; + m_capacity = 0; + return status; + } + Allocator* m_allocator{&globalFudAllocator}; + T* m_data{nullptr}; size_t m_length{0}; size_t m_capacity{0}; }; |