summaryrefslogtreecommitdiff
path: root/include
diff options
context:
space:
mode:
Diffstat (limited to 'include')
-rw-r--r--include/fud_algorithm.hpp36
-rw-r--r--include/fud_array.hpp10
-rw-r--r--include/fud_memory.hpp16
-rw-r--r--include/fud_option.hpp229
-rw-r--r--include/fud_span.hpp10
-rw-r--r--include/fud_status.hpp66
-rw-r--r--include/fud_vector.hpp628
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};
};