From 80806a896b41ee5a4e9241fca1ea0fd79a605a98 Mon Sep 17 00:00:00 2001 From: Andreas Reischuck Date: Sun, 29 Oct 2023 23:26:28 +0100 Subject: [PATCH] array19 cleanup * rename DynamicArrayOf to DynamicArray * rename AllocatedArrayOf to AllocatedArray * rename SliceOf to Span * drop MoveSliceOf - use Span for that * added span methods to simplify array implementations --- Readme.md | 2 +- helpers/array19.natvis | 20 +- helpers/gdbhelpers.py | 17 +- .../array19/AllocatedArray.equals.h | 15 + src/array19.lib/array19/AllocatedArray.h | 116 +++ .../array19/AllocatedArray.ostream.h | 12 + .../array19/AllocatedArrayOf.equals.h | 19 - src/array19.lib/array19/AllocatedArrayOf.h | 138 --- .../array19/AllocatedArrayOf.ostream.h | 22 - .../array19/AllocatedArrayOf.test.cpp | 34 +- src/array19.lib/array19/AllocatedArrayUtils.h | 134 +-- src/array19.lib/array19/Array.h | 45 +- src/array19.lib/array19/Array.ostream.h | 4 +- src/array19.lib/array19/DynamicArray.equals.h | 17 + src/array19.lib/array19/DynamicArray.h | 306 ++++++ .../array19/DynamicArray.ostream.h | 12 + ...ArrayOf.test.cpp => DynamicArray.test.cpp} | 896 +++++++++--------- .../array19/DynamicArrayOf.equals.h | 17 - src/array19.lib/array19/DynamicArrayOf.h | 305 ------ .../array19/DynamicArrayOf.ostream.h | 22 - src/array19.lib/array19/DynamicSortedSet.h | 12 +- src/array19.lib/array19/MoveSliceOf.h | 38 - src/array19.lib/array19/MoveSliceOf.single.h | 11 - src/array19.lib/array19/SliceOf.carray.h | 17 - src/array19.lib/array19/SliceOf.equals.h | 18 - .../array19/SliceOf.equals.test.cpp | 39 - src/array19.lib/array19/SliceOf.h | 42 - src/array19.lib/array19/SliceOf.single.h | 9 - src/array19.lib/array19/SliceOf.store.h | 20 - src/array19.lib/array19/Span.carray.h | 20 + src/array19.lib/array19/Span.equals.h | 21 + src/array19.lib/array19/Span.equals.test.cpp | 39 + src/array19.lib/array19/Span.h | 74 ++ .../array19/{SliceOf.max.h => Span.max.h} | 7 +- src/array19.lib/array19/Span.one.h | 16 + .../{SliceOf.ostream.h => Span.ostream.h} | 4 +- src/array19.lib/array19/Span.store.h | 22 + .../{SliceOf.test.cpp => Span.test.cpp} | 26 +- src/array19.lib/array19/WithIndex.h | 10 +- src/array19.lib/array19/WithIndex.test.cpp | 5 - src/array19.lib/array19/Zip.test.cpp | 5 - src/array19.lib/array19/array19.qbs | 28 +- ...apOf.equals.h => OrderedMapArray.equals.h} | 6 +- .../{OrderedMapOf.h => OrderedMapArray.h} | 320 ++++--- ...Of.ostream.h => OrderedMapArray.ostream.h} | 4 +- .../lookup19/OrderedMapArray.test.cpp | 207 ++++ .../lookup19/OrderedMapOf.test.cpp | 202 ---- src/lookup19.lib/lookup19/OrderedSliceOf.h | 100 -- src/lookup19.lib/lookup19/OrderedSpan.h | 114 +++ src/lookup19.lib/lookup19/lookup19.qbs | 8 +- src/lookup19.lib/lookup19/lookup19.tests.qbs | 2 +- src/partial19.lib/partial19/Partial.h | 10 +- src/serialize19.lib/serialize19/BufferSlice.h | 11 - src/serialize19.lib/serialize19/BufferSpan.h | 11 + .../serialize19/Codec.test.cpp | 2 +- src/serialize19.lib/serialize19/Endianess.h | 2 +- src/serialize19.lib/serialize19/ReadArchive.h | 24 +- .../serialize19/SizeAppender.h | 8 +- src/serialize19.lib/serialize19/SizeArchive.h | 4 +- src/serialize19.lib/serialize19/SliceReader.h | 70 -- src/serialize19.lib/serialize19/SpanReader.h | 70 ++ .../serialize19/UniqueBuffer.h | 12 +- .../serialize19/WriteAppender.h | 12 +- .../serialize19/WriteToArchive.h | 8 +- .../serialize19/dynamicWrite.h | 2 +- ...edArrayOf.h => serialize.AllocatedArray.h} | 50 +- .../serialize19/serialize.BufferSlice.h | 18 - .../serialize19/serialize.BufferSpan.h | 18 + ...test.cpp => serialize.BufferSpan.test.cpp} | 48 +- ...amicArrayOf.h => serialize.DynamicArray.h} | 56 +- ...st.cpp => serialize.DynamicArray.test.cpp} | 68 +- .../serialize19/serialize.Optional.test.cpp | 4 +- .../serialize.PackedOptional.test.cpp | 8 +- .../serialize19/serialize.Partial.test.cpp | 2 +- .../serialize19/serialize.Tuple.test.cpp | 2 +- .../serialize19/serialize.Variant.test.cpp | 2 +- .../serialize19/serialize.std_bitset.test.cpp | 4 +- .../serialize.std_optional.test.cpp | 4 +- .../serialize19/serialize.std_tuple.test.cpp | 2 +- .../serialize.std_variant.test.cpp | 2 +- .../serialize19/serialize.std_vector.h | 2 +- .../serialize19/serialize.std_vector.test.cpp | 6 +- .../serialize19/serialize19.qbs | 10 +- .../serialize19/serialize19.tests.qbs | 4 +- src/signal19.lib/signal19/SignalWith.h | 8 +- src/signal19.lib/signal19/Subscriptions.h | 12 +- 86 files changed, 2085 insertions(+), 2090 deletions(-) create mode 100644 src/array19.lib/array19/AllocatedArray.equals.h create mode 100644 src/array19.lib/array19/AllocatedArray.h create mode 100644 src/array19.lib/array19/AllocatedArray.ostream.h delete mode 100644 src/array19.lib/array19/AllocatedArrayOf.equals.h delete mode 100644 src/array19.lib/array19/AllocatedArrayOf.h delete mode 100644 src/array19.lib/array19/AllocatedArrayOf.ostream.h create mode 100644 src/array19.lib/array19/DynamicArray.equals.h create mode 100644 src/array19.lib/array19/DynamicArray.h create mode 100644 src/array19.lib/array19/DynamicArray.ostream.h rename src/array19.lib/array19/{DynamicArrayOf.test.cpp => DynamicArray.test.cpp} (76%) delete mode 100644 src/array19.lib/array19/DynamicArrayOf.equals.h delete mode 100644 src/array19.lib/array19/DynamicArrayOf.h delete mode 100644 src/array19.lib/array19/DynamicArrayOf.ostream.h delete mode 100644 src/array19.lib/array19/MoveSliceOf.h delete mode 100644 src/array19.lib/array19/MoveSliceOf.single.h delete mode 100644 src/array19.lib/array19/SliceOf.carray.h delete mode 100644 src/array19.lib/array19/SliceOf.equals.h delete mode 100644 src/array19.lib/array19/SliceOf.equals.test.cpp delete mode 100644 src/array19.lib/array19/SliceOf.h delete mode 100644 src/array19.lib/array19/SliceOf.single.h delete mode 100644 src/array19.lib/array19/SliceOf.store.h create mode 100644 src/array19.lib/array19/Span.carray.h create mode 100644 src/array19.lib/array19/Span.equals.h create mode 100644 src/array19.lib/array19/Span.equals.test.cpp create mode 100644 src/array19.lib/array19/Span.h rename src/array19.lib/array19/{SliceOf.max.h => Span.max.h} (52%) create mode 100644 src/array19.lib/array19/Span.one.h rename src/array19.lib/array19/{SliceOf.ostream.h => Span.ostream.h} (73%) create mode 100644 src/array19.lib/array19/Span.store.h rename src/array19.lib/array19/{SliceOf.test.cpp => Span.test.cpp} (63%) rename src/lookup19.lib/lookup19/{OrderedMapOf.equals.h => OrderedMapArray.equals.h} (50%) rename src/lookup19.lib/lookup19/{OrderedMapOf.h => OrderedMapArray.h} (54%) rename src/lookup19.lib/lookup19/{OrderedMapOf.ostream.h => OrderedMapArray.ostream.h} (85%) create mode 100644 src/lookup19.lib/lookup19/OrderedMapArray.test.cpp delete mode 100644 src/lookup19.lib/lookup19/OrderedMapOf.test.cpp delete mode 100644 src/lookup19.lib/lookup19/OrderedSliceOf.h create mode 100644 src/lookup19.lib/lookup19/OrderedSpan.h delete mode 100644 src/serialize19.lib/serialize19/BufferSlice.h create mode 100644 src/serialize19.lib/serialize19/BufferSpan.h delete mode 100644 src/serialize19.lib/serialize19/SliceReader.h create mode 100644 src/serialize19.lib/serialize19/SpanReader.h rename src/serialize19.lib/serialize19/{serialize.AllocatedArrayOf.h => serialize.AllocatedArray.h} (73%) delete mode 100644 src/serialize19.lib/serialize19/serialize.BufferSlice.h create mode 100644 src/serialize19.lib/serialize19/serialize.BufferSpan.h rename src/serialize19.lib/serialize19/{serialize.BufferSlice.test.cpp => serialize.BufferSpan.test.cpp} (61%) rename src/serialize19.lib/serialize19/{serialize.DynamicArrayOf.h => serialize.DynamicArray.h} (76%) rename src/serialize19.lib/serialize19/{serialize.DynamicArrayOf.test.cpp => serialize.DynamicArray.test.cpp} (55%) diff --git a/Readme.md b/Readme.md index 28eb9551..d17a8419 100644 --- a/Readme.md +++ b/Readme.md @@ -24,7 +24,7 @@ Co-Cpp19 contains a lot of sublibraries and is splitted into many small headers. This aims to enable the "pay only what you use policy" of C++ at compile time. * cpp19 - compiler configuration (yours should be somewhat similar) -* array19 - static and dynamic arrays, slices +* array19 - static and dynamic arrays, spans * meta19 - utilities, type, index and pack wrappers and operations * coro19 - basic coroutine with generator support * strong19 - named strong types with tags diff --git a/helpers/array19.natvis b/helpers/array19.natvis index 0ef7a108..13154d03 100644 --- a/helpers/array19.natvis +++ b/helpers/array19.natvis @@ -11,7 +11,7 @@ - + {{empty}} &{m_data,[m_count]na} @@ -21,27 +21,29 @@ - + + {{empty}} - &&{m_data,[m_count]na} + [{m_data,na}] + [{m_count}] m_count m_data + m_capacity - m_count - - {{empty}} - [{m_pointer,na}] - [{m_count}] + + {{empty}} + [{m_data,na}] + [{m_count}] m_count - m_pointer + m_data - m_capacity - m_count diff --git a/helpers/gdbhelpers.py b/helpers/gdbhelpers.py index 44fdd939..d0d73c52 100644 --- a/helpers/gdbhelpers.py +++ b/helpers/gdbhelpers.py @@ -23,7 +23,7 @@ def qdump__array19__Array(d, value): if d.isExpanded(): d.putPlotData(value.address(), count, inner_type) -def qdump__array19__SliceOf(d, value): +def qdump__array19__Span(d, value): inner_type = value.type[0] data = value['m_data'].pointer() count = value['m_count'].integer() @@ -32,18 +32,24 @@ def qdump__array19__SliceOf(d, value): d.checkPointer(data) d.putPlotData(data, count, inner_type) -def qdump__array19__MoveSliceOf(d, value): +def qdump__array19__DynamicArray(d, value): inner_type = value.type[0] data = value['m_data'].pointer() count = value['m_count'].integer() + d.putItemCount(count) + d.putNumChild(count + 1) if count > 0: d.checkPointer(data) - d.putPlotData(data, count, inner_type) + if d.isExpanded(): + with Children(d): + for i in range(count): + d.putSubItem(i, d.createValue(data + i * inner_type.size(), inner_type)) + d.putSubItem('capacity', value["m_capacity"]) -def qdump__array19__DynamicArrayOf(d, value): +def qdump__array19__AllocatedArray(d, value): inner_type = value.type[0] - data = value['m_pointer'].pointer() + data = value['m_data'].pointer() count = value['m_count'].integer() d.putItemCount(count) @@ -54,7 +60,6 @@ def qdump__array19__DynamicArrayOf(d, value): with Children(d): for i in range(count): d.putSubItem(i, d.createValue(data + i * inner_type.size(), inner_type)) - d.putSubItem('capacity', value["m_capacity"]) def qdump__variant19__Variant(d, value): whichValue = value["indexed"]["which"].integer() diff --git a/src/array19.lib/array19/AllocatedArray.equals.h b/src/array19.lib/array19/AllocatedArray.equals.h new file mode 100644 index 00000000..325cbb17 --- /dev/null +++ b/src/array19.lib/array19/AllocatedArray.equals.h @@ -0,0 +1,15 @@ +#pragma once +#include "AllocatedArray.h" +#include "Zip.h" + +namespace array19 { + +template bool operator==(AllocatedArray const& a, AllocatedArray const& b) noexcept { + if (a.count() != b.count()) return false; + for (auto [av, bv] : Zip(a, b)) { + if (!(av == bv)) return false; + } + return true; +} + +} // namespace array19 diff --git a/src/array19.lib/array19/AllocatedArray.h b/src/array19.lib/array19/AllocatedArray.h new file mode 100644 index 00000000..460408b1 --- /dev/null +++ b/src/array19.lib/array19/AllocatedArray.h @@ -0,0 +1,116 @@ +#pragma once +#include "AllocatedArrayUtils.h" +#include "Span.one.h" + +#include // new allocators +#include // size_t +#include // is_nothrow_* + +namespace array19 { + +/// allocated array on the heap +/// * count is fixated at construction time +/// * all array elements are initialized (unlike std::vector) +/// +/// note: +/// * C++ lacks a new T[] (custom-initializer) thing - so we would not be able to copy without default initializing +/// => to work around this we use ::operator new[] & ::operator delete[] and construct explicitly +template struct AllocatedArray final { + using Data = T; + using Count = size_t; + using Index = size_t; + + using ConstIterator = Data const*; + using Iterator = Data*; + using ConstSpan = Span; + using AmendSpan = Span; + using MoveSpan = Span; + +private: + using Utils = AllocatedArrayUtils; + Data* m_data{}; + Count m_count{}; // equals capacity + +public: + AllocatedArray() = default; + ~AllocatedArray() noexcept { + if (m_data) { + Utils::destruct(amend()); + Utils::deallocate(amend()); + } + } + + explicit AllocatedArray(ConstSpan span) : m_data{Utils::allocate(span.count())}, m_count{span.count()} { + Utils::copyConstruct(m_data, span); + } + + explicit AllocatedArray(MoveSpan span) : m_data{Utils::allocate(span.count())}, m_count{span.count()} { + Utils::moveConstruct(m_data, span); + } + + template... Ts> requires(sizeof...(Ts) > 0) + explicit AllocatedArray(Ts&&... args) : m_data{Utils::allocate(sizeof...(Ts))} { + (new (m_data + m_count++) Data{(Ts &&) args}, ...); + } + + AllocatedArray(AllocatedArray const& o) : AllocatedArray(Span{o}) {} + AllocatedArray& operator=(AllocatedArray const& o) { + if (!m_data || o.m_count != m_count) { + *this = AllocatedArray(o); + } + else if (0 < m_count) { + Utils::copyAssign(m_data, o); + } + return *this; + } + + AllocatedArray(AllocatedArray&& o) noexcept : m_data{std::exchange(o.m_data, nullptr)}, m_count{o.m_count} {} + AllocatedArray& operator=(AllocatedArray&& o) noexcept { + if (m_data) { + Utils::destruct(amend()); + Utils::deallocate(amend()); + } + m_data = std::exchange(o.m_data, nullptr); + m_count = o.m_count; + return *this; + } + + [[nodiscard]] static auto createCount(Count count) -> AllocatedArray { + auto result = AllocatedArray{}; + result.m_data = Utils::allocate(count); + Utils::defaultConstruct(AmendSpan{result.m_data, count}); + result.m_count = count; + return result; + } + + [[nodiscard]] auto isEmpty() const noexcept -> bool { return m_count == 0; } + [[nodiscard]] auto count() const noexcept -> Count { return m_count; } + + [[nodiscard]] auto begin() const noexcept -> ConstIterator { return std::launder(m_data); } + [[nodiscard]] auto end() const noexcept -> ConstIterator { return begin() + m_count; } + + // requires: m_data && m_count != 0 + [[nodiscard]] auto front() const -> const Data& { return *begin(); } + // requires: m_data && m_count != 0 + [[nodiscard]] auto back() const -> const Data& { return *(begin() + m_count - 1); } + + // requires: index < m_count + [[nodiscard]] auto operator[](Index index) const noexcept -> Data const& { return *std::launder(m_data + index); } + + [[nodiscard]] operator ConstSpan() const noexcept { return ConstSpan{begin(), m_count}; } + [[nodiscard]] auto amend() noexcept -> AmendSpan { return AmendSpan{std::launder(m_data), m_count}; } + [[nodiscard]] auto move() noexcept -> MoveSpan { return MoveSpan{std::launder(m_data), m_count}; } +}; + +/// simplified deduction guide +/// usage: +/// AllocatedArray{1,2,3} +template AllocatedArray(T&&, Ts&&...) -> AllocatedArray; + +/// deduce Span from AllocatedArray +/// usage: +/// auto a = AllocatedArray{1,2,3}; +/// auto span = Span{a}; +template Span(AllocatedArray const&) -> Span; + +} // namespace array19 diff --git a/src/array19.lib/array19/AllocatedArray.ostream.h b/src/array19.lib/array19/AllocatedArray.ostream.h new file mode 100644 index 00000000..0b8730a9 --- /dev/null +++ b/src/array19.lib/array19/AllocatedArray.ostream.h @@ -0,0 +1,12 @@ +#pragma once +#include "AllocatedArray.h" +#include "Span.ostream.h" + +namespace array19 { + +template +auto operator<<(std::basic_ostream& out, AllocatedArray const& a) -> decltype(out)& { + return out << Span{a}; +} + +} // namespace array19 diff --git a/src/array19.lib/array19/AllocatedArrayOf.equals.h b/src/array19.lib/array19/AllocatedArrayOf.equals.h deleted file mode 100644 index 04e94272..00000000 --- a/src/array19.lib/array19/AllocatedArrayOf.equals.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once -#include "AllocatedArrayOf.h" -#include "Zip.h" - -namespace array19 { - -template bool operator==(const AllocatedArrayOf& a, const AllocatedArrayOf& b) noexcept { - if (a.count() != b.count()) return false; - for (auto [av, bv] : Zip(a, b)) { - if (!(av == bv)) return false; - } - return true; -} - -template bool operator!=(const AllocatedArrayOf& a, const AllocatedArrayOf& b) noexcept { - return !(a == b); -} - -} // namespace array19 diff --git a/src/array19.lib/array19/AllocatedArrayOf.h b/src/array19.lib/array19/AllocatedArrayOf.h deleted file mode 100644 index 67cf07ae..00000000 --- a/src/array19.lib/array19/AllocatedArrayOf.h +++ /dev/null @@ -1,138 +0,0 @@ -#pragma once -#include "AllocatedArrayUtils.h" -#include "MoveSliceOf.h" -#include "SliceOf.single.h" - -#include // new allocators -#include // size_t -#include // is_nothrow_* - -namespace array19 { - -/// allocated array on the heap -/// * count is fixated at construction time -/// * all array elements are initialized (unlike std::vector) -/// -/// note: -/// * C++ lacks a new T[] (custom-initializer) thing - so we would not be able to copy without default initializing -/// => to work around this we use ::operator new[] & ::operator delete[] and construct explicitly -template struct AllocatedArrayOf final { - using Element = T; - using Count = size_t; - using Index = size_t; - - using Iterator = Element*; - using ConstIterator = const Element*; - using Slice = SliceOf; - using ConstSlice = SliceOf; - using MoveSlice = MoveSliceOf; - -private: - using Utils = AllocatedArrayUtils; - T* m_pointer{}; - size_t m_count{}; - -public: - AllocatedArrayOf() = default; - ~AllocatedArrayOf() noexcept { - if (m_pointer) { - Utils::destruct(amend()); - Utils::deallocate(amend()); - } - } - - explicit AllocatedArrayOf(ConstSlice slice) : m_pointer(Utils::allocate(slice.count())), m_count(0) { - Utils::copyConstruct(m_pointer, slice); - m_count = slice.count(); - } - - explicit AllocatedArrayOf(MoveSlice slice) : m_pointer(Utils::allocate(slice.count())), m_count(0) { - Utils::moveConstruct(m_pointer, slice); - m_count = slice.count(); - } - - template requires(sizeof...(Ts) > 0) && requires(Ts&&... args) { (T{(Ts &&) args}, ...); } - explicit AllocatedArrayOf(Ts&&... args) : m_pointer(Utils::allocate(sizeof...(Ts))) - , m_count(0) { - (new (m_pointer + m_count++) T{(Ts &&) args}, ...); - } - - AllocatedArrayOf(const AllocatedArrayOf& o) : AllocatedArrayOf(static_cast(o)) {} - - AllocatedArrayOf& operator=(const AllocatedArrayOf& o) { - if (!m_pointer) { - *this = AllocatedArrayOf(o); - } - else if (o.m_count != m_count) { - Utils::destruct(amend()); - Utils::deallocate(amend()); - if (0 == o.m_count) { - m_pointer = nullptr; - m_count = 0; - } - else { - *this = AllocatedArrayOf(o); - } - } - else if (0 < m_count) { - Utils::copyAssign(m_pointer, o); - } - return *this; - } - - AllocatedArrayOf(AllocatedArrayOf&& o) noexcept // - : m_pointer(o.m_pointer) - , m_count(o.m_count) { - o.m_pointer = nullptr; - } - AllocatedArrayOf& operator=(AllocatedArrayOf&& o) noexcept { - if (m_pointer) { - Utils::destruct(amend()); - Utils::deallocate(amend()); - } - m_pointer = o.m_pointer; - m_count = o.m_count; - o.m_pointer = nullptr; - return *this; - } - - [[nodiscard]] static auto createCount(Count count) -> AllocatedArrayOf { - - auto result = AllocatedArrayOf{}; - if (count > 0) { - result.m_pointer = Utils::allocate(count); - Utils::defaultConstruct(Slice{result.m_pointer, count}); - result.m_count = count; - } - return result; - } - - [[nodiscard]] auto isEmpty() const noexcept -> bool { return m_count == 0; } - [[nodiscard]] auto count() const noexcept -> Count { return m_count; } - - [[nodiscard]] auto front() const -> const T& { return *begin(); } - [[nodiscard]] auto back() const -> const T& { return *(begin() + m_count - 1); } - - [[nodiscard]] auto begin() const noexcept -> ConstIterator { return std::launder(m_pointer); } - [[nodiscard]] auto end() const noexcept -> ConstIterator { return begin() + m_count; } - [[nodiscard]] auto operator[](Index index) const noexcept -> const T& { return *std::launder(m_pointer + index); } - operator ConstSlice() const noexcept { return SliceOf{begin(), m_count}; } - - // hint: use `amend()` if you need to iterate and mutate - [[nodiscard]] auto amendBegin() noexcept -> Iterator { return std::launder(m_pointer); } - [[nodiscard]] auto amendEnd() noexcept -> Iterator { return amendBegin() + m_count; } - [[nodiscard]] auto amend() noexcept -> Slice { return SliceOf{amendBegin(), m_count}; } -}; - -/// simplified deduction guide -/// usage: -/// AllocatedArrayOf{1,2,3} -template AllocatedArrayOf(T&&, Ts&&...) -> AllocatedArrayOf; - -/// deduce SliceOf from AllocatedArrayOf -/// usage: -/// auto a = AllocatedArrayOf{1,2,3}; -/// auto slice = SliceOf{a}; -template SliceOf(const AllocatedArrayOf&) -> SliceOf; - -} // namespace array19 diff --git a/src/array19.lib/array19/AllocatedArrayOf.ostream.h b/src/array19.lib/array19/AllocatedArrayOf.ostream.h deleted file mode 100644 index 87982621..00000000 --- a/src/array19.lib/array19/AllocatedArrayOf.ostream.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once -#include "AllocatedArrayOf.h" - -#include - -namespace array19 { - -template -auto operator<<(std::basic_ostream& out, const AllocatedArrayOf& a) -> decltype(out)& { - out << "["; - bool first = true; - for (auto& v : a) { - if (first) - first = false; - else - out << ", "; - out << v; - } - return out << "]"; -} - -} // namespace array19 diff --git a/src/array19.lib/array19/AllocatedArrayOf.test.cpp b/src/array19.lib/array19/AllocatedArrayOf.test.cpp index 5129fefc..c7aa7e22 100644 --- a/src/array19.lib/array19/AllocatedArrayOf.test.cpp +++ b/src/array19.lib/array19/AllocatedArrayOf.test.cpp @@ -1,25 +1,25 @@ -#include "AllocatedArrayOf.h" +#include "AllocatedArray.h" -#include "AllocatedArrayOf.equals.h" -#include "AllocatedArrayOf.ostream.h" -#include "SliceOf.carray.h" -#include "SliceOf.equals.h" -#include "SliceOf.ostream.h" +#include "AllocatedArray.equals.h" +#include "AllocatedArray.ostream.h" +#include "Span.carray.h" +#include "Span.equals.h" +#include "Span.ostream.h" #include #include using namespace array19; -TEST(AllocatedArrayOf, construct) { - auto v = AllocatedArrayOf{1, 2, 3}; - ASSERT_EQ(sliceOfCArray({1, 2, 3}), SliceOf{v}); +TEST(AllocatedArray, construct) { + auto v = AllocatedArray{1, 2, 3}; + ASSERT_EQ(spanOfCArray({1, 2, 3}), Span{v}); } namespace { struct NonTrivial; -using NonTrivialArray = AllocatedArrayOf; +using NonTrivialArray = AllocatedArray; struct User { NonTrivialArray v; @@ -48,13 +48,13 @@ struct NonTrivial { } // namespace -TEST(AllocatedArrayOf, NontrivialExample) { - auto v = AllocatedArrayOf{}; +TEST(AllocatedArray, NontrivialExample) { + auto v = AllocatedArray{}; EXPECT_TRUE(v.isEmpty()); ASSERT_EQ(v.count(), 0u); - v = AllocatedArrayOf{NonTrivial{}, NonTrivial{2}}; + v = AllocatedArray{NonTrivial{}, NonTrivial{2}}; ASSERT_EQ(v.count(), 2u); @@ -62,9 +62,9 @@ TEST(AllocatedArrayOf, NontrivialExample) { ASSERT_EQ(v, v2); } -TEST(AllocatedArrayOf, MoveAssignAfterMove) { +TEST(AllocatedArray, MoveAssignAfterMove) { using E = NonTrivial; - using AA = AllocatedArrayOf; + using AA = AllocatedArray; auto v = AA{E{1}}; auto v2 = std::move(v); @@ -74,9 +74,9 @@ TEST(AllocatedArrayOf, MoveAssignAfterMove) { ASSERT_EQ(v, (AA{E{2}, E{3}})); } -TEST(AllocatedArrayOf, CopyAssignAfterMove) { +TEST(AllocatedArray, CopyAssignAfterMove) { using E = NonTrivial; - using AA = AllocatedArrayOf; + using AA = AllocatedArray; auto v = AA{E{1}}; auto v2 = std::move(v); diff --git a/src/array19.lib/array19/AllocatedArrayUtils.h b/src/array19.lib/array19/AllocatedArrayUtils.h index f9272daa..943470e9 100644 --- a/src/array19.lib/array19/AllocatedArrayUtils.h +++ b/src/array19.lib/array19/AllocatedArrayUtils.h @@ -1,6 +1,5 @@ #pragma once -#include "MoveSliceOf.h" -#include "SliceOf.h" +#include "Span.h" #include // ::operator new, ::operator delete #include // size_t @@ -10,19 +9,21 @@ namespace array19 { +template concept will_construct = requires(T&& arg) { B{(T &&) arg}; }; + /// Utility functions that implement the meat of allocated arrays /// /// note: /// * This replaces the algorithmns form std header. -/// * It works with Slices instead of iterators to simplify the interface +/// * It works with spans instead of iterators to simplify the interface template struct AllocatedArrayUtils { static_assert(std::is_nothrow_move_constructible_v, "Please ensure move constructior is marked noexcept!"); static_assert(std::is_nothrow_move_assignable_v, "Please ensure move assignment is marked noexcept!"); static_assert(std::is_nothrow_destructible_v, "Please ensure destructor is marked noexcept!"); - using Slice = SliceOf; - using ConstSlice = SliceOf; - using MoveSlice = MoveSliceOf; + using ConstSpan = Span; + using AmendSpan = Span; + using MoveSpan = Span; /// returns uninitialized storage for \param count elements with proper alignment /// note: @@ -38,143 +39,144 @@ template struct AllocatedArrayUtils { } } - /// frees the storage pointed to by the slice + /// frees the storage pointed to by the span /// note: - /// * slice.begin() has to be the pointer returned by allocate(size.count()) + /// * span.begin() has to be the pointer returned by allocate(size.count()) /// * exceptions form delete will terminate - we assume memory is corrupted! - static void deallocate(Slice slice) noexcept { + static void deallocate(AmendSpan span) noexcept { #if __cpp_sized_deallocation if constexpr (__STDCPP_DEFAULT_NEW_ALIGNMENT__ < alignof(T)) { - ::operator delete[](slice.begin(), slice.count() * sizeof(T), std::align_val_t{alignof(T)}); + ::operator delete[](span.begin(), span.count() * sizeof(T), std::align_val_t{alignof(T)}); } else { - ::operator delete[](slice.begin(), slice.count() * sizeof(T)); + ::operator delete[](span.begin(), span.count() * sizeof(T)); } #else if constexpr (__STDCPP_DEFAULT_NEW_ALIGNMENT__ < alignof(T)) { - ::operator delete[](slice.begin(), std::align_val_t{alignof(T)}); + ::operator delete[](span.begin(), std::align_val_t{alignof(T)}); } else { - ::operator delete[](slice.begin()); + ::operator delete[](span.begin()); } #endif } - /// default construct every element of \param slice if necessary - static void defaultConstruct(Slice slice) noexcept(std::is_nothrow_default_constructible_v) { - if constexpr (!std::is_trivially_default_constructible_v) - for (auto& e : slice) new (&e) T(); + /// default construct every element of \param span if necessary + static void defaultConstruct(AmendSpan span) noexcept(std::is_nothrow_default_constructible_v) { + if constexpr (!std::is_trivially_default_constructible_v) { + for (auto& e : span) new (&e) T(); + } } - /// calls destructor for every element of \param slice if necessary + /// calls destructor for every element of \param span if necessary /// note: /// * we assume destructors are always noexcept - static void destruct(Slice slice) noexcept { + static void destruct(AmendSpan span) noexcept { if constexpr (!std::is_trivially_destructible_v) - for (auto& elem : slice) elem.~T(); + for (auto& elem : span) elem.~T(); } - /// constructs a copy of \param fromSlice at \param toPointer + /// constructs a copy of \param fromSpan at \param toPointer /// note: - /// * toPointer has to point to least fromSlice.count() elements - /// * assumes toPointer and fromSlice do not overlap - static void copyConstruct(T* toPointer, ConstSlice fromSlice) noexcept(std::is_nothrow_copy_constructible_v) { + /// * toPointer has to point to least fromSpan.count() elements + /// * assumes toPointer and fromSpan do not overlap + static void copyConstruct(T* toPointer, ConstSpan fromSpan) noexcept(std::is_nothrow_copy_constructible_v) { if constexpr (std::is_trivially_copy_constructible_v) { - if (!fromSlice.isEmpty()) { - memcpy(toPointer, fromSlice.begin(), fromSlice.count() * sizeof(T)); + if (!fromSpan.isEmpty()) { + memcpy(toPointer, fromSpan.begin(), fromSpan.count() * sizeof(T)); } } else { - for (auto& from : fromSlice) new (toPointer++) T(from); + for (auto& from : fromSpan) new (toPointer++) T(from); } } - /// move constructs elements of \param fromSlice into \param to Pointer + /// move constructs elements of \param fromSpan into \param to Pointer /// note: - /// * toPointer has to point to least fromSlice.count() elements - /// * assumes toPointer and fromSlice do not overlap + /// * toPointer has to point to least fromSpan.count() elements + /// * assumes toPointer and fromSpan do not overlap /// * we assume move is always noexcept! - static void moveConstruct(T* toPointer, MoveSlice fromSlice) noexcept { + static void moveConstruct(T* toPointer, MoveSpan fromSpan) noexcept { if constexpr (std::is_trivially_move_constructible_v) { - if (!fromSlice.isEmpty()) { - memcpy(toPointer, fromSlice.begin(), fromSlice.count() * sizeof(T)); + if (!fromSpan.isEmpty()) { + memcpy(toPointer, fromSpan.begin(), fromSpan.count() * sizeof(T)); } } else { - for (auto& from : fromSlice) new (toPointer++) T(std::move(from)); + for (auto& from : fromSpan) new (toPointer++) T(std::move(from)); } } - /// assigns elements of \param fromSlice into \param toPointer + /// assigns elements of \param fromSpan into \param toPointer /// note: - /// * toPointer has to point to least fromSlice.count() initialized elements - /// * assumes toPointer and fromSlice do not overlap - static void copyAssign(T* toPointer, ConstSlice fromSlice) noexcept(std::is_nothrow_copy_assignable_v) { + /// * toPointer has to point to least fromSpan.count() initialized elements + /// * assumes toPointer and fromSpan do not overlap + static void copyAssign(T* toPointer, ConstSpan fromSpan) noexcept(std::is_nothrow_copy_assignable_v) { if constexpr (std::is_trivially_copy_assignable_v) { - if (!fromSlice.isEmpty()) { - memcpy(toPointer, fromSlice.begin(), fromSlice.count() * sizeof(T)); + if (!fromSpan.isEmpty()) { + memcpy(toPointer, fromSpan.begin(), fromSpan.count() * sizeof(T)); } } else { - for (const auto& from : fromSlice) *toPointer++ = from; + for (const auto& from : fromSpan) *toPointer++ = from; } } - /// move assigns elements of \param fromSlice into \param to Pointer + /// move assigns elements of \param fromSpan into \param to Pointer /// note: - /// * toPointer has to point to least fromSlice.count() elements - /// * assumes toPointer and fromSlice do not overlap + /// * toPointer has to point to least fromSpan.count() elements + /// * assumes toPointer and fromSpan do not overlap /// * we assume move is always noexcept! - static void moveAssign(T* toPointer, MoveSlice fromSlice) noexcept { + static void moveAssign(T* toPointer, MoveSpan fromSpan) noexcept { if constexpr (std::is_trivially_move_assignable_v) { - if (!fromSlice.isEmpty()) { - memcpy(toPointer, fromSlice.begin(), fromSlice.count() * sizeof(T)); + if (!fromSpan.isEmpty()) { + memcpy(toPointer, fromSpan.begin(), fromSpan.count() * sizeof(T)); } } else { - for (auto& from : fromSlice) *toPointer++ = std::move(from); + for (auto& from : fromSpan) *toPointer++ = std::move(from); } } - /// move assigns elements of \param fromSlice into \param to Pointer + /// move assigns elements of \param fromSpan into \param to Pointer /// note: - /// * toPointer has to point to least fromSlice.count() elements - /// * toPointer has to be before fromSlice.begin() if ranges overlap + /// * toPointer has to point to least fromSpan.count() elements + /// * toPointer has to be before fromSpan.begin() if ranges overlap /// * we assume move is always noexcept! /// /// example: /// [ e e e e e e e ] - /// toPointer ^ ^ fromSlice.begin() - static void moveAssignForward(T* toPointer, MoveSlice fromSlice) noexcept { + /// toPointer ^ ^ fromSpan.begin() + static void moveAssignForward(T* toPointer, MoveSpan fromSpan) noexcept { if constexpr (std::is_trivially_move_constructible_v) { - if (!fromSlice.isEmpty()) { - memmove(toPointer, fromSlice.begin(), fromSlice.count() * sizeof(T)); + if (!fromSpan.isEmpty()) { + memmove(toPointer, fromSpan.begin(), fromSpan.count() * sizeof(T)); } } else { - for (auto& from : fromSlice) *toPointer++ = std::move(from); + for (auto& from : fromSpan) *toPointer++ = std::move(from); } } - /// move assigns elements of \param fromSlice into \param to Pointer + /// move assigns elements of \param fromSpan into \param to Pointer /// note: - /// * toPointer has to point to least fromSlice.count() elements - /// * toPointer has to be behind fromSlice.begin() if ranges overlap + /// * toPointer has to point to least fromSpan.count() elements + /// * toPointer has to be behind fromSpan.begin() if ranges overlap /// * we assume move is always noexcept! /// /// example: /// [ e e e e e e e ] - /// fromSlice.begin() ^ ^ toPointer - static void moveAssignReverse(T* toPointer, MoveSlice fromSlice) noexcept { + /// fromSpan.begin() ^ ^ toPointer + static void moveAssignReverse(T* toPointer, MoveSpan fromSpan) noexcept { if constexpr (std::is_trivially_move_assignable_v) { - if (!fromSlice.isEmpty()) { - memmove(toPointer, fromSlice.begin(), fromSlice.count() * sizeof(T)); + if (!fromSpan.isEmpty()) { + memmove(toPointer, fromSpan.begin(), fromSpan.count() * sizeof(T)); } } else { - auto rTo = toPointer + fromSlice.count(); - auto rFrom = fromSlice.end(); - auto rFromEnd = fromSlice.begin(); + auto rTo = toPointer + fromSpan.count(); + auto rFrom = fromSpan.end(); + auto rFromEnd = fromSpan.begin(); while (rFrom != rFromEnd) *(--rTo) = std::move(*(--rFrom)); } } diff --git a/src/array19.lib/array19/Array.h b/src/array19.lib/array19/Array.h index c4f81eb4..fe8ffc05 100644 --- a/src/array19.lib/array19/Array.h +++ b/src/array19.lib/array19/Array.h @@ -1,6 +1,5 @@ #pragma once -#include "MoveSliceOf.h" -#include "SliceOf.h" +#include "Span.h" #include // size_t @@ -9,43 +8,41 @@ namespace array19 { /// simplified version of std::array /// * no member types /// * no exceptions -template struct Array { +template struct Array { using Element = T; - static constexpr auto count = C; + static constexpr auto count = N; T m[count]; bool operator==(const Array&) const = default; [[nodiscard]] constexpr auto isEmpty() const noexcept -> bool { return false; } - [[nodiscard]] constexpr auto begin() const noexcept -> const T* { return m; } - [[nodiscard]] constexpr auto end() const noexcept -> const T* { return m + C; } - [[nodiscard]] constexpr auto operator[](size_t i) const noexcept -> const T& { return m[i]; } - [[nodiscard]] constexpr operator SliceOf() const& noexcept { return SliceOf{m, C}; } - [[nodiscard]] constexpr auto move() noexcept -> MoveSliceOf { return MoveSliceOf{m, C}; } - - // hint: use `amendSliceOfArray()` if you need to iterate and mutate - [[nodiscard]] constexpr auto amendBegin() noexcept -> T* { return m; } - [[nodiscard]] constexpr auto amendEnd() noexcept -> T* { return m + C; } - [[nodiscard]] constexpr auto amend() noexcept -> SliceOf { return SliceOf{m, C}; } + + [[nodiscard]] constexpr auto begin() const noexcept -> T const* { return m; } + [[nodiscard]] constexpr auto end() const noexcept -> T const* { return m + count; } + + // requires: index < count + [[nodiscard]] constexpr auto operator[](size_t index) const noexcept -> T const& { return m[index]; } + + [[nodiscard]] constexpr operator Span() const& noexcept { return Span{m, count}; } + [[nodiscard]] constexpr auto amend() noexcept -> Span { return Span{m, count}; } + [[nodiscard]] constexpr auto move() noexcept -> Span { return Span{m, count}; } }; template struct Array { using Element = T; - static constexpr auto count = 0u; + static constexpr auto count = size_t{}; bool operator==(const Array&) const = default; [[nodiscard]] constexpr auto isEmpty() const noexcept -> bool { return true; } - [[nodiscard]] constexpr auto begin() const noexcept -> const T* { return nullptr; } - [[nodiscard]] constexpr auto end() const noexcept -> const T* { return nullptr; } - [[nodiscard]] constexpr operator SliceOf() const& noexcept { return SliceOf{begin(), 0u}; } - [[nodiscard]] constexpr auto move() noexcept -> MoveSliceOf { return MoveSliceOf{amendBegin(), 0u}; } - - // hint: use `amendSliceOfArray()` if you need to iterate and mutate - [[nodiscard]] constexpr auto amendBegin() noexcept -> T* { return nullptr; } - [[nodiscard]] constexpr auto amendEnd() noexcept -> T* { return nullptr; } - [[nodiscard]] constexpr auto amend() noexcept -> SliceOf { return SliceOf{amendBegin(), 0u}; } + + [[nodiscard]] constexpr auto begin() const noexcept -> T const* { return nullptr; } + [[nodiscard]] constexpr auto end() const noexcept -> T const* { return nullptr; } + + [[nodiscard]] constexpr operator Span() const& noexcept { return Span{nullptr, count}; } + [[nodiscard]] constexpr auto amend() noexcept -> Span { return Span{nullptr, count}; } + [[nodiscard]] constexpr auto move() noexcept -> Span { return Span{nullptr, count}; } }; /// simplified deduction guide diff --git a/src/array19.lib/array19/Array.ostream.h b/src/array19.lib/array19/Array.ostream.h index 4d812503..138f46d9 100644 --- a/src/array19.lib/array19/Array.ostream.h +++ b/src/array19.lib/array19/Array.ostream.h @@ -6,8 +6,8 @@ namespace array19 { -template -auto operator<<(std::basic_ostream& out, const Array& a) -> decltype(out)& { +template +auto operator<<(std::basic_ostream& out, Array const& a) -> decltype(out)& { out << "["; bool first = true; for (auto& v : a) { diff --git a/src/array19.lib/array19/DynamicArray.equals.h b/src/array19.lib/array19/DynamicArray.equals.h new file mode 100644 index 00000000..eebc9ce3 --- /dev/null +++ b/src/array19.lib/array19/DynamicArray.equals.h @@ -0,0 +1,17 @@ +#pragma once +#include "DynamicArray.h" +#include "Zip.h" + +namespace array19 { + +template bool operator==(DynamicArray const& a, DynamicArray const& b) noexcept { + if (a.count() != b.count()) return false; + for (auto [av, bv] : Zip(a, b)) { + if (!(av == bv)) return false; + } + return true; +} + +template bool operator!=(DynamicArray const& a, DynamicArray const& b) noexcept { return !(a == b); } + +} // namespace array19 diff --git a/src/array19.lib/array19/DynamicArray.h b/src/array19.lib/array19/DynamicArray.h new file mode 100644 index 00000000..95797412 --- /dev/null +++ b/src/array19.lib/array19/DynamicArray.h @@ -0,0 +1,306 @@ +#pragma once +#include "AllocatedArrayUtils.h" +#include "Span.one.h" + +#include // launder +#include // size_t + +namespace array19 { + +/// replacement for std::vector with different API +/// * focus on Span +/// * mutable is explicit - use amend() +template struct DynamicArray final { + using Data = T; + using Count = size_t; + using Index = size_t; + + using ConstIterator = Data const*; + using AmendIterator = Data*; + using ConstSpan = Span; + using AmendSpan = Span; + using MoveSpan = Span; + +private: + using Utils = AllocatedArrayUtils; + + Data* m_data{}; + Count m_count{}; + Count m_capacity{}; + +public: + DynamicArray() = default; + ~DynamicArray() noexcept { + if (m_data) { + Utils::destruct(amend()); + Utils::deallocate(_amendCapacity()); + } + } + + explicit DynamicArray(ConstSpan span) + : m_data{Utils::allocate(span.count())} + , m_count{span.count()} + , m_capacity{span.count()} { + Utils::copyConstruct(m_data, span); + } + + explicit DynamicArray(MoveSpan span) + : m_data{Utils::allocate(span.count())} + , m_count{span.count()} + , m_capacity{span.count()} { + Utils::moveConstruct(m_data, span); + } + + template... Ts> requires(sizeof...(Ts) > 0) + explicit DynamicArray(Ts&&... args) : m_data{Utils::allocate(sizeof...(Ts))} + , m_capacity{sizeof...(Ts)} { + (new (m_data + m_count++) Data{(Ts &&) args}, ...); + } + + DynamicArray(DynamicArray const& o) + : m_data{Utils::allocate(o.m_count)} + , m_count{o.m_count} + , m_capacity{o.m_count} { + Utils::copyConstruct(m_data, o); + } + DynamicArray& operator=(DynamicArray const& o) { + if (!m_data || m_capacity < o.m_count) { + *this = DynamicArray(o); + } + else { + if (m_count > o.m_count) { + Utils::copyAssign(_amendBegin(), o); + Utils::destruct(amend().skip(o.m_count)); + } + else { + Utils::copyAssign(_amendBegin(), ConstSpan{o}.head(m_count)); + Utils::copyConstruct(_vacantBegin(), ConstSpan{o}.skip(m_count)); + } + m_count = o.m_count; + } + return *this; + } + + DynamicArray(DynamicArray&& o) noexcept + : m_data(std::exchange(o.m_data, nullptr)) + , m_count(o.m_count) + , m_capacity(o.m_capacity) {} + DynamicArray& operator=(DynamicArray&& o) noexcept { + if (m_data) { + Utils::destruct(amend()); + Utils::deallocate(_amendCapacity()); + } + m_data = std::exchange(o.m_data, nullptr); + m_count = o.m_count; + m_capacity = o.m_capacity; + return *this; + } + + [[nodiscard]] constexpr auto isEmpty() const noexcept -> bool { return m_count == 0; } + [[nodiscard]] auto count() const -> Count { return m_count; } + [[nodiscard]] auto totalCapacity() const -> Count { return m_capacity; } + [[nodiscard]] auto unusedCapacity() const -> Count { return m_capacity - m_count; } + + // requires: m_count > 0 + [[nodiscard]] auto front() const -> Data const& { return *begin(); } + // requires: m_count > 0 + [[nodiscard]] auto back() const -> Data const& { return *(begin() + m_count - 1); } + + [[nodiscard]] auto begin() const noexcept -> ConstIterator { return std::launder(m_data); } + [[nodiscard]] auto end() const noexcept -> ConstIterator { return begin() + m_count; } + + // requires: index < m_count + [[nodiscard]] auto operator[](Index index) const noexcept -> Data const& { return *(begin() + index); } + + [[nodiscard]] operator ConstSpan() const noexcept { return ConstSpan{begin(), m_count}; } + [[nodiscard]] auto amend() -> AmendSpan { return AmendSpan{_amendBegin(), m_count}; } + [[nodiscard]] auto move() -> MoveSpan { return MoveSpan{_amendBegin(), m_count}; } + + void ensureCapacity(Count count) { + if (totalCapacity() < count) _growBy(count - totalCapacity()); + } + void ensureUnusedCapacity(Count count) { + if (unusedCapacity() < count) _growBy(count - unusedCapacity()); + } + + void clear() { + Utils::destruct(amend()); + m_count = 0; + } + + /// append a single element constructed in place with the given arguments + template auto emplace_back(Ts&&... args) -> Data& { + ensureUnusedCapacity(1); + auto ptr = new (_vacantBegin()) Data{(Ts &&) args...}; + m_count++; + return *ptr; + } + + /// appends possibly multiple elements at the end + void append(ConstSpan span) { + ensureUnusedCapacity(span.count()); + Utils::copyConstruct(_vacantBegin(), span); + m_count += span.count(); + } + + /// appends possibly multiple elements at the end + void appendMoved(MoveSpan span) { + ensureUnusedCapacity(span.count()); + Utils::moveConstruct(_vacantBegin(), span); + m_count += span.count(); + } + + /// appends count default constructed elements + void appendCount(Count count) { + ensureUnusedCapacity(count); + Utils::defaultConstruct(_amendVacant().head(count)); + m_count += count; + } + + /// removes the last element + void pop() { + Utils::destruct(amend().tail(1)); + m_count--; + } + + /// swiss army knife of mutating an dynamic array + /// replaces: + /// * insert - splice(it, 0, spanOfOne{V}) + /// * erase - splice(it, N, {}) + /// extensions: + /// * insert of elements at once + /// * combined remove and insert + void splice(ConstIterator cIt, Count removeCount, ConstSpan insertSpan) { + auto const offset = static_cast(cIt - begin()); + auto const it = _amendBegin() + offset; + auto const insertCount = insertSpan.count(); + auto const remainCount = static_cast(m_count - offset - removeCount); + auto const remainSpan = move().tail(remainCount); + // old: [ ..(offset)..,[it] ..(removeCount).., ..(remainCount)... ] + // new: [ ..(offset)..,[it] ..(insertCount).., ..(remainCount)... ] + + auto const newCount = static_cast(m_count - removeCount) + insertCount; + if (m_capacity < newCount) { // not enough storage => arrange everything in new storage + auto const newStorage = _grownStorage(insertCount - removeCount); + Utils::moveConstruct(newStorage.begin(), move().head(offset)); + Utils::copyConstruct(newStorage.begin() + offset, insertSpan); + Utils::moveConstruct(newStorage.begin() + offset + insertCount, remainSpan); + Utils::destruct(amend()); + Utils::deallocate(_amendCapacity()); + m_data = newStorage.begin(); + m_capacity = newStorage.count(); + } + else if (m_count >= newCount) { // shrinking + auto const shrinkCount = static_cast(m_count - newCount); + Utils::copyAssign(it, insertSpan); + Utils::moveAssignForward(m_data + offset + insertCount, remainSpan); + Utils::destruct(amend().tail(shrinkCount)); + } + else if (offset + insertCount <= m_count) { // parts of remainSpan is moved beyond end() + auto const growCount = static_cast(newCount - m_count); + Utils::moveConstruct(_vacantBegin(), remainSpan.tail(growCount)); + Utils::moveAssignReverse(it + insertCount, remainSpan.drop_tail(growCount)); + Utils::copyAssign(it, insertSpan); + } + else { // remainSpan is moved beyond end() + Utils::moveConstruct(m_data + offset + insertCount, remainSpan); + auto const assignElems = static_cast(m_count - offset); + Utils::copyAssign(it, insertSpan.head(assignElems)); + Utils::copyConstruct(_vacantBegin(), insertSpan.skip(assignElems)); + } + m_count = newCount; + } + + /// Same as splice but moves inserted elements into the array + void spliceMoved(ConstIterator cIt, Count removeCount, MoveSpan insertSpan) { + auto const offset = static_cast(cIt - begin()); + auto const it = _amendBegin() + offset; + auto const insertCount = insertSpan.count(); + auto const remainCount = static_cast(m_count - offset - removeCount); + auto const remainSpan = move().tail(remainCount); + // old: [ ..(offset)..,[it] ..(removeCount).., ..(remainCount)... ] + // new: [ ..(offset)..,[it] ..(insertCount).., ..(remainCount)... ] + + auto const newCount = static_cast(m_count - removeCount) + insertCount; + if (m_capacity < newCount) { // not enough storage => arrange everything in new storage + auto const newStorage = _grownStorage(insertCount - removeCount); + Utils::moveConstruct(newStorage.begin(), move().head(offset)); + Utils::moveConstruct(newStorage.begin() + offset, insertSpan); + Utils::moveConstruct(newStorage.begin() + offset + insertCount, remainSpan); + Utils::destruct(amend()); + Utils::deallocate(_amendCapacity()); + m_data = newStorage.begin(); + m_capacity = newStorage.count(); + } + else if (m_count >= newCount) { // shrinking + auto const shrinkCount = static_cast(m_count - newCount); + Utils::moveAssign(it, insertSpan); + Utils::moveAssignForward(m_data + offset + insertCount, remainSpan); + Utils::destruct(amend().tail(shrinkCount)); + } + else if (offset + insertCount <= m_count) { // parts of remainSpan is moved beyond end() + auto const growCount = static_cast(newCount - m_count); + Utils::moveConstruct(_vacantBegin(), remainSpan.tail(growCount)); + Utils::moveAssignReverse(it + insertCount, remainSpan.drop_tail(growCount)); + Utils::moveAssign(it, insertSpan); + } + else { // remainSpan is moved beyond end() + Utils::moveConstruct(m_data + offset + insertCount, remainSpan); + auto const assignElems = static_cast(m_count - offset); + Utils::moveAssign(it, insertSpan.head(assignElems)); + Utils::moveConstruct(_vacantBegin(), insertSpan.skip(assignElems)); + } + m_count = newCount; + } + + /// Same as splice but moves inserted elements into the array + void remove(AmendIterator it, Count removeCount) { + auto offset = it - _amendBegin(); + auto remainCount = m_count - offset - removeCount; + auto remainSpan = MoveSpan{it + removeCount, remainCount}; + + Utils::moveAssignForward(m_data + offset, remainSpan); + Utils::destruct(AmendSpan{amend().end() - removeCount, removeCount}); + m_count = m_count - removeCount; + } + +private: + [[nodiscard]] auto _amendBegin() -> AmendIterator { return std::launder(m_data); } + [[nodiscard]] auto _vacantBegin() -> AmendIterator { return m_data + m_count; } + [[nodiscard]] auto _amendCapacity() -> AmendSpan { return AmendSpan{m_data, m_capacity}; } + [[nodiscard]] auto _amendVacant() -> AmendSpan { return _amendCapacity().skip(m_count); } + + [[nodiscard]] auto _grownStorage(size_t growBy) const -> AmendSpan { + auto const cur = m_capacity; + auto res = (cur << 1) - (cur >> 1) + (cur >> 4); // * 1.563 + if (res < 5) res = 5; + if (res < m_capacity + growBy) res = m_capacity + growBy; + auto ptr = Utils::allocate(res); + return AmendSpan{ptr, res}; + } + void _growBy(size_t by) { + auto const newStorage = _grownStorage(by); + Utils::moveConstruct(newStorage.begin(), move()); + Utils::destruct(amend()); + Utils::deallocate(AmendSpan{m_data, m_capacity}); + m_data = newStorage.begin(); + m_capacity = newStorage.count(); + } +}; + +/// simplified deduction guide +/// usage: +/// DynamicArray{1,2,3} +template DynamicArray(T&&, Ts&&...) -> DynamicArray; + +/// deduce T from span +template DynamicArray(Span) -> DynamicArray; +template DynamicArray(Span) -> DynamicArray; + +/// deduce Span from DynamicArray +/// usage: +/// auto a = DynamicArray{1,2,3; +/// auto span = Span{a}; +template Span(const DynamicArray&) -> Span; + +} // namespace array19 diff --git a/src/array19.lib/array19/DynamicArray.ostream.h b/src/array19.lib/array19/DynamicArray.ostream.h new file mode 100644 index 00000000..a93d4ad9 --- /dev/null +++ b/src/array19.lib/array19/DynamicArray.ostream.h @@ -0,0 +1,12 @@ +#pragma once +#include "DynamicArray.h" +#include "Span.ostream.h" + +namespace array19 { + +template +auto operator<<(std::basic_ostream& out, DynamicArray const& a) -> decltype(out)& { + return out << Span{a}; +} + +} // namespace array19 diff --git a/src/array19.lib/array19/DynamicArrayOf.test.cpp b/src/array19.lib/array19/DynamicArray.test.cpp similarity index 76% rename from src/array19.lib/array19/DynamicArrayOf.test.cpp rename to src/array19.lib/array19/DynamicArray.test.cpp index 01b6b11f..4efefee1 100644 --- a/src/array19.lib/array19/DynamicArrayOf.test.cpp +++ b/src/array19.lib/array19/DynamicArray.test.cpp @@ -1,448 +1,448 @@ -#include "DynamicArrayOf.h" - -#include "Array.h" -#include "DynamicArrayOf.equals.h" -#include "DynamicArrayOf.ostream.h" -#include "SliceOf.carray.h" -#include "SliceOf.equals.h" - -#include -#include -#include - -using namespace array19; - -TEST(DynamicArrayOf, intExample) { - - auto v = DynamicArrayOf{}; - - EXPECT_TRUE(v.isEmpty()); - EXPECT_EQ(v.totalCapacity(), 0u); - EXPECT_EQ(v.unusedCapacity(), 0u); - ASSERT_EQ(v.count(), 0u); - - v.ensureUnusedCapacity(1); - - EXPECT_GE(v.totalCapacity(), 1u); - EXPECT_GE(v.unusedCapacity(), 1u); - ASSERT_EQ(v.count(), 0u); - - v.append(sliceOfCArray({12, 23})); - - ASSERT_EQ(v.count(), 2u); - EXPECT_GE(v.totalCapacity(), 2u); - - v.append(sliceOfCArray({42, 64})); - - ASSERT_EQ(v.count(), 4u); - - v.splice(v.amendBegin() + 1, 2, sliceOfCArray({99})); - - auto v2 = DynamicArrayOf{}; - v2.append(sliceOfCArray({12, 99, 64})); - ASSERT_EQ(v, v2); - - v2.append(sliceOfCArray({45})); - v2 = v; // copy - ASSERT_EQ(v, v2); - - v.append(sliceOfCArray({45})); - v2 = std::move(v); - ASSERT_EQ(sliceOfCArray({12, 99, 64, 45}), SliceOf{v2}); -} - -TEST(DynamicArrayOf, construct) { - auto v = DynamicArrayOf{1, 2, 3}; - ASSERT_EQ(sliceOfCArray({1, 2, 3}), SliceOf{v}); -} - -namespace { - -struct NonTrivial; -using NonTrivialArray = DynamicArrayOf; - -struct User { - NonTrivialArray v; - - User() = default; -}; - -struct NonTrivial { - NonTrivial() : v(1) {} - explicit NonTrivial(int v) : v(v) {} - NonTrivial(const NonTrivial& o) : v(o.v) {} - NonTrivial& operator=(const NonTrivial& o) { return v = o.v, *this; } - NonTrivial(NonTrivial&& o) noexcept : v(o.v) {} - NonTrivial& operator=(NonTrivial&& o) noexcept { return v = o.v, *this; } - ~NonTrivial() noexcept { c = false; } - bool operator==(const NonTrivial&) const = default; - - int value() const { return v; } - -private: - bool c{true}; - int v; -}; - -[[maybe_unused]] auto operator<<(std::ostream& out, const NonTrivial& c) -> std::ostream& { return out << c.value(); } - -} // namespace - -TEST(DynamicArrayOf, NontrivialExample) { - auto v = DynamicArrayOf{}; - - EXPECT_TRUE(v.isEmpty()); - EXPECT_EQ(v.totalCapacity(), 0u); - EXPECT_EQ(v.unusedCapacity(), 0u); - ASSERT_EQ(v.count(), 0u); - - v.ensureUnusedCapacity(1u); - - EXPECT_GE(v.totalCapacity(), 1u); - EXPECT_GE(v.unusedCapacity(), 1u); - ASSERT_EQ(v.count(), 0u); - - v.append(sliceOfCArray({NonTrivial{12}, NonTrivial{23}})); - - ASSERT_EQ(v.count(), 2u); - EXPECT_GE(v.totalCapacity(), 2u); - - v.append(sliceOfCArray({NonTrivial{42}, NonTrivial{64}})); - - ASSERT_EQ(v.count(), 4u); - - v.splice(v.amendBegin() + 1, 2, sliceOfCArray({NonTrivial{99}})); - - auto v2 = DynamicArrayOf{}; - v2.append(sliceOfCArray({NonTrivial{12}, NonTrivial{99}, NonTrivial{64}})); - ASSERT_EQ(v, v2); -} - -TEST(DynamicArrayOf, MoveAssignAfterMove) { - using E = NonTrivial; - using AA = DynamicArrayOf; - auto v = AA{E{1}}; - - auto v2 = std::move(v); - ASSERT_EQ(v2, (AA{E{1}})); - - v = AA{E{2}, E{3}}; - ASSERT_EQ(v, (AA{E{2}, E{3}})); -} - -TEST(DynamicArrayOf, CopyAssignAfterMove) { - using E = NonTrivial; - using AA = DynamicArrayOf; - auto v = AA{E{1}}; - - auto v2 = std::move(v); - ASSERT_EQ(v2, (AA{E{1}})); - - auto v3 = AA{E{2}, E{3}}; - v = v3; - ASSERT_EQ(v, (AA{E{2}, E{3}})); -} - -namespace { - -struct Vec3 { - double x, y, z; - - bool operator==(const Vec3&) const = default; - - [[maybe_unused]] friend auto operator<<(std::ostream& o, const Vec3& v) -> std::ostream& { - return o << "x:" << v.x << ", y:" << v.y << ", z:" << v.z; - } -}; - -} // namespace - -TEST(DynamicArrayOf, EmplaceStruct) { - auto v = DynamicArrayOf{}; - v.emplace_back(1.1, 2.2, 3.3); - - auto v2 = DynamicArrayOf{Vec3{1.1, 2.2, 3.3}}; - EXPECT_EQ(v, v2); -} - -namespace { - -struct Recorder { - static inline std::stringstream store; - int v{}; - - Recorder(int v) : v(v) { store << v << ".ctor\n"; } - - Recorder() { store << "{}.ctor\n"; } - ~Recorder() noexcept { store << v << ".dtor\n"; } - Recorder(const Recorder& o) : v(100 + o.v) { store << v << ".ctor(const&)\n"; } - Recorder& operator=(const Recorder& o) { - return (store << v << ".op=(const& " << (100 + o.v) << ")\n"), v = 100 + o.v, *this; - } - Recorder(Recorder&& o) noexcept : v(o.v) { o.v = 1000 + v, store << v << ".ctor(&&)\n"; } - Recorder& operator=(Recorder&& o) noexcept { - return (store << v << ".op=(&& " << o.v << ")\n"), v = o.v, o.v = 1000 + v, *this; - } - - bool operator==(const Recorder&) const = default; - - [[maybe_unused]] friend auto operator<<(std::ostream& o, const Recorder& r) -> std::ostream& { return o << r.v; } -}; - -} // namespace - -TEST(DynamicArrayOf, RecordedCopyAndMove) { - using T = DynamicArrayOf; - auto v2 = T{}; - { - Recorder::store = std::stringstream{}; - - auto v = T{}; - v.ensureCapacity(5); - ASSERT_EQ(v.unusedCapacity(), 5u); // 5 is currently the minimum - - EXPECT_EQ(Recorder::store.str(), "") << "No elements are constructed yet"; - - Recorder::store = std::stringstream{}; - v.emplace_back(1); - v.emplace_back(2); - v.emplace_back(3); - v.emplace_back(4); - v.emplace_back(5); - - EXPECT_EQ(Recorder::store.str(), R"(1.ctor -2.ctor -3.ctor -4.ctor -5.ctor -)") << "5 elements constructed"; - - Recorder::store = std::stringstream{}; - ASSERT_EQ(v.unusedCapacity(), 0u) << "all capacity used"; - v.emplace_back(6); - ASSERT_NE(v.unusedCapacity(), 0u) << "capacity grows by more than one"; - - EXPECT_EQ(Recorder::store.str(), R"(1.ctor(&&) -2.ctor(&&) -3.ctor(&&) -4.ctor(&&) -5.ctor(&&) -1001.dtor -1002.dtor -1003.dtor -1004.dtor -1005.dtor -6.ctor -)") << "moved 5 elements to new store, destructed old store, constructed new element"; - - Recorder::store = std::stringstream{}; - v2 = std::move(v); - ASSERT_EQ(Recorder::store.str(), "") << "move of array does not move elements"; - } - ASSERT_EQ(Recorder::store.str(), "") << "destruct of moved from array has no influence"; - { - auto v = v2; - EXPECT_EQ(Recorder::store.str(), R"(101.ctor(const&) -102.ctor(const&) -103.ctor(const&) -104.ctor(const&) -105.ctor(const&) -106.ctor(const&) -)") << "copy of array copied all 6 elements"; - - Recorder::store = std::stringstream{}; - } - EXPECT_EQ(Recorder::store.str(), R"(101.dtor -102.dtor -103.dtor -104.dtor -105.dtor -106.dtor -)") << "destruction of copy destroys copied elements"; - - { - Recorder::store = std::stringstream{}; - - auto v = T{Array{Recorder{1}, 2, 3}.move()}; - EXPECT_EQ(Recorder::store.str(), R"(1.ctor -2.ctor -3.ctor -1.ctor(&&) -2.ctor(&&) -3.ctor(&&) -1003.dtor -1002.dtor -1001.dtor -)") << "construct static array, move to dynamic array, destroy static array"; - } -} - -TEST(DynamicArrayOf, RecordedSpliceGrowOverCapacity) { - using T = DynamicArrayOf; - - Recorder::store = std::stringstream{}; - auto v = T{1, 2, 3, 4, 5}; - EXPECT_EQ(Recorder::store.str(), R"(1.ctor -2.ctor -3.ctor -4.ctor -5.ctor -)") << "5 array elements constructed"; - - Recorder::store = std::stringstream{}; - auto insert = Array{Recorder{12}, 13}; - EXPECT_EQ(Recorder::store.str(), R"(12.ctor -13.ctor -)") << "2 array elements constructed"; - - Recorder::store = std::stringstream{}; - ASSERT_EQ(v.unusedCapacity(), 0u) << "all capacity used"; - v.splice(v.amendBegin() + 1, 1, insert); - EXPECT_EQ(Recorder::store.str(), R"(1.ctor(&&) -112.ctor(const&) -113.ctor(const&) -3.ctor(&&) -4.ctor(&&) -5.ctor(&&) -1001.dtor -2.dtor -1003.dtor -1004.dtor -1005.dtor -)") << "1,3,4,5 is moved to new location; 12,13 are copied between; old location is destructed"; - EXPECT_EQ(v, (T{1, 112, 113, 3, 4, 5})); -} - -TEST(DynamicArrayOf, RecordedSpliceShrink) { - using T = DynamicArrayOf; - - Recorder::store = std::stringstream{}; - auto v = T{1, 2, 3, 4, 5}; - EXPECT_EQ(Recorder::store.str(), R"(1.ctor -2.ctor -3.ctor -4.ctor -5.ctor -)") << "5 array elements constructed"; - - Recorder::store = std::stringstream{}; - auto insert = Array{Recorder{12}, 13}; - EXPECT_EQ(Recorder::store.str(), R"(12.ctor -13.ctor -)") << "2 array elements constructed"; - - Recorder::store = std::stringstream{}; - v.splice(v.amendBegin() + 1, 3, insert); - EXPECT_EQ(Recorder::store.str(), R"(2.op=(const& 112) -3.op=(const& 113) -4.op=(&& 5) -1005.dtor -)"); - - EXPECT_EQ(v, (T{1, 112, 113, 5})); -} - -TEST(DynamicArrayOf, RecordedSpliceInsertOverEnd) { - using T = DynamicArrayOf; - - Recorder::store = std::stringstream{}; - auto v = T{}; - v.emplace_back(1); - v.emplace_back(2); - v.emplace_back(3); - EXPECT_EQ(Recorder::store.str(), R"(1.ctor -2.ctor -3.ctor -)") << "3 array elements constructed"; - - Recorder::store = std::stringstream{}; - auto insert = Array{Recorder{12}, 13, 14}; - EXPECT_EQ(Recorder::store.str(), R"(12.ctor -13.ctor -14.ctor -)") << "3 array elements constructed"; - - Recorder::store = std::stringstream{}; - ASSERT_EQ(v.unusedCapacity(), insert.count - 1) << "all capacity used"; - v.splice(v.amendBegin() + 1, 1, insert); - EXPECT_EQ(Recorder::store.str(), R"(3.ctor(&&) -2.op=(const& 112) -1003.op=(const& 113) -114.ctor(const&) -)"); - EXPECT_EQ(v, (T{1, 112, 113, 114, 3})); -} - -TEST(DynamicArrayOf, RecordedSpliceInsertBeforeEnd) { - using T = DynamicArrayOf; - - Recorder::store = std::stringstream{}; - auto v = T{}; - v.emplace_back(1); - v.emplace_back(2); - v.emplace_back(3); - v.emplace_back(4); - EXPECT_EQ(Recorder::store.str(), R"(1.ctor -2.ctor -3.ctor -4.ctor -)") << "4 array elements constructed"; - - Recorder::store = std::stringstream{}; - auto insert = Array{Recorder{12}, 13}; - EXPECT_EQ(Recorder::store.str(), R"(12.ctor -13.ctor -)") << "2 array elements constructed"; - - Recorder::store = std::stringstream{}; - ASSERT_EQ(v.unusedCapacity(), insert.count - 1) << "all capacity used"; - v.splice(v.amendBegin() + 1, 1, insert); - EXPECT_EQ(Recorder::store.str(), R"(4.ctor(&&) -1004.op=(&& 3) -2.op=(const& 112) -1003.op=(const& 113) -)"); - EXPECT_EQ(v, (T{1, 112, 113, 3, 4})); -} - -TEST(DynamicArrayOf, RecordedSpliceRemoveFirst) { - using T = DynamicArrayOf; - - Recorder::store = std::stringstream{}; - auto v = T{}; - v.emplace_back(1); - v.emplace_back(2); - v.emplace_back(3); - EXPECT_EQ(Recorder::store.str(), R"(1.ctor -2.ctor -3.ctor -)") << "3 array elements constructed"; - - Recorder::store = std::stringstream{}; - v.splice(v.amendBegin(), 1, {}); - EXPECT_EQ(Recorder::store.str(), R"(1.op=(&& 2) -1002.op=(&& 3) -1003.dtor -)"); - EXPECT_EQ(v, (T{2, 3})); -} - -TEST(DynamicArrayOf, RecordedSpliceRemoveLast) { - using T = DynamicArrayOf; - - Recorder::store = std::stringstream{}; - auto v = T{}; - v.emplace_back(1); - v.emplace_back(2); - v.emplace_back(3); - EXPECT_EQ(Recorder::store.str(), R"(1.ctor -2.ctor -3.ctor -)") << "3 array elements constructed"; - - Recorder::store = std::stringstream{}; - v.splice(v.amendBegin() + 2, 1, {}); - EXPECT_EQ(Recorder::store.str(), R"(3.dtor -)"); - EXPECT_EQ(v, (T{1, 2})); -} +#include "DynamicArray.h" + +#include "Array.h" +#include "DynamicArray.equals.h" +#include "DynamicArray.ostream.h" +#include "Span.carray.h" +#include "Span.equals.h" + +#include +#include +#include + +using namespace array19; + +TEST(DynamicArray, intExample) { + + auto v = DynamicArray{}; + + EXPECT_TRUE(v.isEmpty()); + EXPECT_EQ(v.totalCapacity(), 0u); + EXPECT_EQ(v.unusedCapacity(), 0u); + ASSERT_EQ(v.count(), 0u); + + v.ensureUnusedCapacity(1); + + EXPECT_GE(v.totalCapacity(), 1u); + EXPECT_GE(v.unusedCapacity(), 1u); + ASSERT_EQ(v.count(), 0u); + + v.append(spanOfCArray({12, 23})); + + ASSERT_EQ(v.count(), 2u); + EXPECT_GE(v.totalCapacity(), 2u); + + v.append(spanOfCArray({42, 64})); + + ASSERT_EQ(v.count(), 4u); + + v.splice(v.amend().begin() + 1, 2, spanOfCArray({99})); + + auto v2 = DynamicArray{}; + v2.append(spanOfCArray({12, 99, 64})); + ASSERT_EQ(v, v2); + + v2.append(spanOfCArray({45})); + v2 = v; // copy + ASSERT_EQ(v, v2); + + v.append(spanOfCArray({45})); + v2 = std::move(v); + ASSERT_EQ(spanOfCArray({12, 99, 64, 45}), Span{v2}); +} + +TEST(DynamicArray, construct) { + auto v = DynamicArray{1, 2, 3}; + ASSERT_EQ(spanOfCArray({1, 2, 3}), Span{v}); +} + +namespace { + +struct NonTrivial; +using NonTrivialArray = DynamicArray; + +struct User { + NonTrivialArray v; + + User() = default; +}; + +struct NonTrivial { + NonTrivial() : v(1) {} + explicit NonTrivial(int v) : v(v) {} + NonTrivial(const NonTrivial& o) : v(o.v) {} + NonTrivial& operator=(const NonTrivial& o) { return v = o.v, *this; } + NonTrivial(NonTrivial&& o) noexcept : v(o.v) {} + NonTrivial& operator=(NonTrivial&& o) noexcept { return v = o.v, *this; } + ~NonTrivial() noexcept { c = false; } + bool operator==(const NonTrivial&) const = default; + + int value() const { return v; } + +private: + bool c{true}; + int v; +}; + +[[maybe_unused]] auto operator<<(std::ostream& out, const NonTrivial& c) -> std::ostream& { return out << c.value(); } + +} // namespace + +TEST(DynamicArray, NontrivialExample) { + auto v = DynamicArray{}; + + EXPECT_TRUE(v.isEmpty()); + EXPECT_EQ(v.totalCapacity(), 0u); + EXPECT_EQ(v.unusedCapacity(), 0u); + ASSERT_EQ(v.count(), 0u); + + v.ensureUnusedCapacity(1u); + + EXPECT_GE(v.totalCapacity(), 1u); + EXPECT_GE(v.unusedCapacity(), 1u); + ASSERT_EQ(v.count(), 0u); + + v.append(spanOfCArray({NonTrivial{12}, NonTrivial{23}})); + + ASSERT_EQ(v.count(), 2u); + EXPECT_GE(v.totalCapacity(), 2u); + + v.append(spanOfCArray({NonTrivial{42}, NonTrivial{64}})); + + ASSERT_EQ(v.count(), 4u); + + v.splice(v.amend().begin() + 1, 2, spanOfCArray({NonTrivial{99}})); + + auto v2 = DynamicArray{}; + v2.append(spanOfCArray({NonTrivial{12}, NonTrivial{99}, NonTrivial{64}})); + ASSERT_EQ(v, v2); +} + +TEST(DynamicArray, MoveAssignAfterMove) { + using E = NonTrivial; + using AA = DynamicArray; + auto v = AA{E{1}}; + + auto v2 = std::move(v); + ASSERT_EQ(v2, (AA{E{1}})); + + v = AA{E{2}, E{3}}; + ASSERT_EQ(v, (AA{E{2}, E{3}})); +} + +TEST(DynamicArray, CopyAssignAfterMove) { + using E = NonTrivial; + using AA = DynamicArray; + auto v = AA{E{1}}; + + auto v2 = std::move(v); + ASSERT_EQ(v2, (AA{E{1}})); + + auto v3 = AA{E{2}, E{3}}; + v = v3; + ASSERT_EQ(v, (AA{E{2}, E{3}})); +} + +namespace { + +struct Vec3 { + double x, y, z; + + bool operator==(const Vec3&) const = default; + + [[maybe_unused]] friend auto operator<<(std::ostream& o, const Vec3& v) -> std::ostream& { + return o << "x:" << v.x << ", y:" << v.y << ", z:" << v.z; + } +}; + +} // namespace + +TEST(DynamicArray, EmplaceStruct) { + auto v = DynamicArray{}; + v.emplace_back(1.1, 2.2, 3.3); + + auto v2 = DynamicArray{Vec3{1.1, 2.2, 3.3}}; + EXPECT_EQ(v, v2); +} + +namespace { + +struct Recorder { + static inline std::stringstream store; + int v{}; + + Recorder(int v) : v(v) { store << v << ".ctor\n"; } + + Recorder() { store << "{}.ctor\n"; } + ~Recorder() noexcept { store << v << ".dtor\n"; } + Recorder(const Recorder& o) : v(100 + o.v) { store << v << ".ctor(const&)\n"; } + Recorder& operator=(const Recorder& o) { + return (store << v << ".op=(const& " << (100 + o.v) << ")\n"), v = 100 + o.v, *this; + } + Recorder(Recorder&& o) noexcept : v(o.v) { o.v = 1000 + v, store << v << ".ctor(&&)\n"; } + Recorder& operator=(Recorder&& o) noexcept { + return (store << v << ".op=(&& " << o.v << ")\n"), v = o.v, o.v = 1000 + v, *this; + } + + bool operator==(const Recorder&) const = default; + + [[maybe_unused]] friend auto operator<<(std::ostream& o, const Recorder& r) -> std::ostream& { return o << r.v; } +}; + +} // namespace + +TEST(DynamicArray, RecordedCopyAndMove) { + using T = DynamicArray; + auto v2 = T{}; + { + Recorder::store = std::stringstream{}; + + auto v = T{}; + v.ensureCapacity(5); + ASSERT_EQ(v.unusedCapacity(), 5u); // 5 is currently the minimum + + EXPECT_EQ(Recorder::store.str(), "") << "No elements are constructed yet"; + + Recorder::store = std::stringstream{}; + v.emplace_back(1); + v.emplace_back(2); + v.emplace_back(3); + v.emplace_back(4); + v.emplace_back(5); + + EXPECT_EQ(Recorder::store.str(), R"(1.ctor +2.ctor +3.ctor +4.ctor +5.ctor +)") << "5 elements constructed"; + + Recorder::store = std::stringstream{}; + ASSERT_EQ(v.unusedCapacity(), 0u) << "all capacity used"; + v.emplace_back(6); + ASSERT_NE(v.unusedCapacity(), 0u) << "capacity grows by more than one"; + + EXPECT_EQ(Recorder::store.str(), R"(1.ctor(&&) +2.ctor(&&) +3.ctor(&&) +4.ctor(&&) +5.ctor(&&) +1001.dtor +1002.dtor +1003.dtor +1004.dtor +1005.dtor +6.ctor +)") << "moved 5 elements to new store, destructed old store, constructed new element"; + + Recorder::store = std::stringstream{}; + v2 = std::move(v); + ASSERT_EQ(Recorder::store.str(), "") << "move of array does not move elements"; + } + ASSERT_EQ(Recorder::store.str(), "") << "destruct of moved from array has no influence"; + { + auto v = v2; + EXPECT_EQ(Recorder::store.str(), R"(101.ctor(const&) +102.ctor(const&) +103.ctor(const&) +104.ctor(const&) +105.ctor(const&) +106.ctor(const&) +)") << "copy of array copied all 6 elements"; + + Recorder::store = std::stringstream{}; + } + EXPECT_EQ(Recorder::store.str(), R"(101.dtor +102.dtor +103.dtor +104.dtor +105.dtor +106.dtor +)") << "destruction of copy destroys copied elements"; + + { + Recorder::store = std::stringstream{}; + + auto v = T{Array{Recorder{1}, 2, 3}.move()}; + EXPECT_EQ(Recorder::store.str(), R"(1.ctor +2.ctor +3.ctor +1.ctor(&&) +2.ctor(&&) +3.ctor(&&) +1003.dtor +1002.dtor +1001.dtor +)") << "construct static array, move to dynamic array, destroy static array"; + } +} + +TEST(DynamicArray, RecordedSpliceGrowOverCapacity) { + using T = DynamicArray; + + Recorder::store = std::stringstream{}; + auto v = T{1, 2, 3, 4, 5}; + EXPECT_EQ(Recorder::store.str(), R"(1.ctor +2.ctor +3.ctor +4.ctor +5.ctor +)") << "5 array elements constructed"; + + Recorder::store = std::stringstream{}; + auto insert = Array{Recorder{12}, 13}; + EXPECT_EQ(Recorder::store.str(), R"(12.ctor +13.ctor +)") << "2 array elements constructed"; + + Recorder::store = std::stringstream{}; + ASSERT_EQ(v.unusedCapacity(), 0u) << "all capacity used"; + v.splice(v.amend().begin() + 1, 1, insert); + EXPECT_EQ(Recorder::store.str(), R"(1.ctor(&&) +112.ctor(const&) +113.ctor(const&) +3.ctor(&&) +4.ctor(&&) +5.ctor(&&) +1001.dtor +2.dtor +1003.dtor +1004.dtor +1005.dtor +)") << "1,3,4,5 is moved to new location; 12,13 are copied between; old location is destructed"; + EXPECT_EQ(v, (T{1, 112, 113, 3, 4, 5})); +} + +TEST(DynamicArray, RecordedSpliceShrink) { + using T = DynamicArray; + + Recorder::store = std::stringstream{}; + auto v = T{1, 2, 3, 4, 5}; + EXPECT_EQ(Recorder::store.str(), R"(1.ctor +2.ctor +3.ctor +4.ctor +5.ctor +)") << "5 array elements constructed"; + + Recorder::store = std::stringstream{}; + auto insert = Array{Recorder{12}, 13}; + EXPECT_EQ(Recorder::store.str(), R"(12.ctor +13.ctor +)") << "2 array elements constructed"; + + Recorder::store = std::stringstream{}; + v.splice(v.amend().begin() + 1, 3, insert); + EXPECT_EQ(Recorder::store.str(), R"(2.op=(const& 112) +3.op=(const& 113) +4.op=(&& 5) +1005.dtor +)"); + + EXPECT_EQ(v, (T{1, 112, 113, 5})); +} + +TEST(DynamicArray, RecordedSpliceInsertOverEnd) { + using T = DynamicArray; + + Recorder::store = std::stringstream{}; + auto v = T{}; + v.emplace_back(1); + v.emplace_back(2); + v.emplace_back(3); + EXPECT_EQ(Recorder::store.str(), R"(1.ctor +2.ctor +3.ctor +)") << "3 array elements constructed"; + + Recorder::store = std::stringstream{}; + auto insert = Array{Recorder{12}, 13, 14}; + EXPECT_EQ(Recorder::store.str(), R"(12.ctor +13.ctor +14.ctor +)") << "3 array elements constructed"; + + Recorder::store = std::stringstream{}; + ASSERT_EQ(v.unusedCapacity(), insert.count - 1) << "all capacity used"; + v.splice(v.amend().begin() + 1, 1, insert); + EXPECT_EQ(Recorder::store.str(), R"(3.ctor(&&) +2.op=(const& 112) +1003.op=(const& 113) +114.ctor(const&) +)"); + EXPECT_EQ(v, (T{1, 112, 113, 114, 3})); +} + +TEST(DynamicArray, RecordedSpliceInsertBeforeEnd) { + using T = DynamicArray; + + Recorder::store = std::stringstream{}; + auto v = T{}; + v.emplace_back(1); + v.emplace_back(2); + v.emplace_back(3); + v.emplace_back(4); + EXPECT_EQ(Recorder::store.str(), R"(1.ctor +2.ctor +3.ctor +4.ctor +)") << "4 array elements constructed"; + + Recorder::store = std::stringstream{}; + auto insert = Array{Recorder{12}, 13}; + EXPECT_EQ(Recorder::store.str(), R"(12.ctor +13.ctor +)") << "2 array elements constructed"; + + Recorder::store = std::stringstream{}; + ASSERT_EQ(v.unusedCapacity(), insert.count - 1) << "all capacity used"; + v.splice(v.amend().begin() + 1, 1, insert); + EXPECT_EQ(Recorder::store.str(), R"(4.ctor(&&) +1004.op=(&& 3) +2.op=(const& 112) +1003.op=(const& 113) +)"); + EXPECT_EQ(v, (T{1, 112, 113, 3, 4})); +} + +TEST(DynamicArray, RecordedSpliceRemoveFirst) { + using T = DynamicArray; + + Recorder::store = std::stringstream{}; + auto v = T{}; + v.emplace_back(1); + v.emplace_back(2); + v.emplace_back(3); + EXPECT_EQ(Recorder::store.str(), R"(1.ctor +2.ctor +3.ctor +)") << "3 array elements constructed"; + + Recorder::store = std::stringstream{}; + v.splice(v.amend().begin(), 1, {}); + EXPECT_EQ(Recorder::store.str(), R"(1.op=(&& 2) +1002.op=(&& 3) +1003.dtor +)"); + EXPECT_EQ(v, (T{2, 3})); +} + +TEST(DynamicArray, RecordedSpliceRemoveLast) { + using T = DynamicArray; + + Recorder::store = std::stringstream{}; + auto v = T{}; + v.emplace_back(1); + v.emplace_back(2); + v.emplace_back(3); + EXPECT_EQ(Recorder::store.str(), R"(1.ctor +2.ctor +3.ctor +)") << "3 array elements constructed"; + + Recorder::store = std::stringstream{}; + v.splice(v.amend().begin() + 2, 1, {}); + EXPECT_EQ(Recorder::store.str(), R"(3.dtor +)"); + EXPECT_EQ(v, (T{1, 2})); +} diff --git a/src/array19.lib/array19/DynamicArrayOf.equals.h b/src/array19.lib/array19/DynamicArrayOf.equals.h deleted file mode 100644 index fb956504..00000000 --- a/src/array19.lib/array19/DynamicArrayOf.equals.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once -#include "DynamicArrayOf.h" -#include "Zip.h" - -namespace array19 { - -template bool operator==(const DynamicArrayOf& a, const DynamicArrayOf& b) noexcept { - if (a.count() != b.count()) return false; - for (auto [av, bv] : Zip(a, b)) { - if (!(av == bv)) return false; - } - return true; -} - -template bool operator!=(const DynamicArrayOf& a, const DynamicArrayOf& b) noexcept { return !(a == b); } - -} // namespace array19 diff --git a/src/array19.lib/array19/DynamicArrayOf.h b/src/array19.lib/array19/DynamicArrayOf.h deleted file mode 100644 index 4dcbdd54..00000000 --- a/src/array19.lib/array19/DynamicArrayOf.h +++ /dev/null @@ -1,305 +0,0 @@ -#pragma once -#include "AllocatedArrayUtils.h" -#include "MoveSliceOf.h" -#include "SliceOf.single.h" - -#include // launder -#include // size_t - -namespace array19 { - -/// replacement for std::vector with different API -/// * focus on SliceOf -/// * mutable is explicit - use amend() -template struct DynamicArrayOf final { - using Element = T; - using Count = size_t; - using Index = size_t; - - using Iterator = Element*; - using ConstIterator = const Element*; - using Slice = SliceOf; - using ConstSlice = SliceOf; - using MoveSlice = MoveSliceOf; - -private: - using Utils = AllocatedArrayUtils; - - T* m_pointer{}; - Count m_count{}; - Count m_capacity{}; - -public: - DynamicArrayOf() = default; - ~DynamicArrayOf() noexcept { - if (m_pointer) { - Utils::destruct(amend()); - Utils::deallocate(Slice{m_pointer, m_capacity}); - } - } - - explicit DynamicArrayOf(ConstSlice slice) : m_pointer(Utils::allocate(slice.count())), m_capacity(slice.count()) { - Utils::copyConstruct(m_pointer, slice); - m_count = slice.count(); - } - - explicit DynamicArrayOf(MoveSlice slice) : m_pointer(Utils::allocate(slice.count())), m_capacity(slice.count()) { - Utils::moveConstruct(m_pointer, slice); - m_count = slice.count(); - } - - template requires(sizeof...(Ts) > 0) && requires(Ts&&... args) { (T{(Ts &&) args}, ...); } - explicit DynamicArrayOf(Ts&&... args) : m_pointer(Utils::allocate(sizeof...(Ts))) - , m_capacity(sizeof...(Ts)) { - (new (m_pointer + m_count++) T{(Ts &&) args}, ...); - } - - DynamicArrayOf(const DynamicArrayOf& o) - : m_pointer(Utils::allocate(o.m_count)) - , m_count(o.m_count) - , m_capacity(o.m_count) { - Utils::copyConstruct(m_pointer, o); - } - DynamicArrayOf& operator=(const DynamicArrayOf& o) { - if (!m_pointer || m_capacity < o.m_count) { - *this = DynamicArrayOf(o); - } - else { - if (m_count > o.m_count) { - Utils::copyAssign(amendBegin(), o); - Utils::destruct(Slice{amendBegin() + o.m_count, m_count - o.m_count}); - } - else { - Utils::copyAssign(amendBegin(), ConstSlice{o.begin(), m_count}); - Utils::copyConstruct(storageEnd(), ConstSlice{o.begin() + m_count, o.m_count - m_count}); - } - m_count = o.m_count; - } - return *this; - } - - DynamicArrayOf(DynamicArrayOf&& o) noexcept // - : m_pointer(o.m_pointer) - , m_count(o.m_count) - , m_capacity(o.m_capacity) { - o.m_pointer = nullptr; - } - DynamicArrayOf& operator=(DynamicArrayOf&& o) noexcept { - if (m_pointer) { - Utils::destruct(amend()); - Utils::deallocate(Slice{m_pointer, m_capacity}); - } - m_pointer = o.m_pointer; - m_count = o.m_count; - m_capacity = o.m_capacity; - o.m_pointer = nullptr; - return *this; - } - - [[nodiscard]] constexpr auto isEmpty() const noexcept -> bool { return m_count == 0; } - [[nodiscard]] auto count() const -> Count { return m_count; } - [[nodiscard]] auto totalCapacity() const -> Count { return m_capacity; } - [[nodiscard]] auto unusedCapacity() const -> Count { return m_capacity - m_count; } - - [[nodiscard]] auto front() const -> const T& { return *begin(); } - [[nodiscard]] auto back() const -> const T& { return *(begin() + m_count - 1); } - - [[nodiscard]] auto begin() const noexcept -> ConstIterator { - return std::launder(reinterpret_cast(m_pointer)); - } - [[nodiscard]] auto end() const noexcept -> ConstIterator { return begin() + m_count; } - [[nodiscard]] auto operator[](Index index) const noexcept -> const Element& { - return *std::launder(reinterpret_cast(m_pointer + index)); - } - [[nodiscard]] operator ConstSlice() const noexcept { return ConstSlice{begin(), m_count}; } - - [[nodiscard]] auto amendBegin() & noexcept -> Iterator { return std::launder(reinterpret_cast(m_pointer)); } - [[nodiscard]] auto amendEnd() & noexcept -> Iterator { return amendBegin() + m_count; } - - [[nodiscard]] auto amend() -> Slice { return Slice{amendBegin(), m_count}; } - [[nodiscard]] auto move() -> MoveSlice { return MoveSlice{amendBegin(), m_count}; } - - void ensureCapacity(Count count) { - if (totalCapacity() < count) growBy(count - totalCapacity()); - } - void ensureUnusedCapacity(Count count) { - if (unusedCapacity() < count) growBy(count - unusedCapacity()); - } - - void clear() { - Utils::destruct(amend()); - m_count = 0; - } - - /// append a single element constructed in place with the given arguments - template auto emplace_back(Ts&&... args) -> T& { - ensureUnusedCapacity(1); - auto ptr = new (storageEnd()) Element{(Ts &&) args...}; - m_count++; - return *ptr; - } - - /// appends possibly multiple elements at the end - void append(ConstSlice elems) { - ensureUnusedCapacity(elems.count()); - Utils::copyConstruct(storageEnd(), elems); - m_count += elems.count(); - } - - /// appends possibly multiple elements at the end - void appendMoved(MoveSlice elems) { - ensureUnusedCapacity(elems.count()); - Utils::moveConstruct(storageEnd(), elems); - m_count += elems.count(); - } - - /// appends count default constructed elements - void appendCount(size_t count) { - ensureUnusedCapacity(count); - Utils::defaultConstruct(Slice{storageEnd(), count}); - m_count += count; - } - - /// removes the last element - void pop() { - Utils::destruct(Slice{amendBegin() + m_count - 1, 1}); - m_count--; - } - - /// swiss army knife of mutating an dynamic array - /// replaces: - /// * insert - splice(it, 0, sliceOfSingle{V}) - /// * erase - splice(it, N, {}) - /// extensions: - /// * insert of elements at once - /// * combined remove and insert - void splice(Iterator it, Count removeCount, ConstSlice insertSlice) { - auto offset = it - amendBegin(); - auto insertCount = insertSlice.count(); - auto remainCount = m_count - offset - removeCount; - auto remainSlice = MoveSlice{it + removeCount, remainCount}; - // old: [ ..(offset)..,[it] ..(removeCount).., ..(remainCount)... ] - // new: [ ..(offset)..,[it] ..(insertCount).., ..(remainCount)... ] - - auto newCount = (m_count - removeCount) + insertCount; - if (m_capacity < newCount) { // not enough storage => arrange everything in new storage - auto newStorage = grownStorage(insertCount - removeCount); - Utils::moveConstruct(newStorage.begin(), MoveSlice{amendBegin(), static_cast(offset)}); - Utils::copyConstruct(newStorage.begin() + offset, insertSlice); - Utils::moveConstruct(newStorage.begin() + offset + insertCount, remainSlice); - Utils::destruct(amend()); - Utils::deallocate(Slice{m_pointer, m_capacity}); - m_pointer = newStorage.begin(); - m_capacity = newStorage.count(); - } - else if (m_count >= newCount) { // shrinking - auto shrinkCount = m_count - newCount; - Utils::copyAssign(it, insertSlice); - Utils::moveAssignForward(m_pointer + offset + insertCount, remainSlice); - Utils::destruct(Slice{amendEnd() - shrinkCount, shrinkCount}); - } - else if (offset + insertCount <= m_count) { // parts of remainSlice is moved beyond end() - auto growCount = newCount - m_count; - Utils::moveConstruct(storageEnd(), remainSlice.slice(remainCount - growCount, growCount)); - Utils::moveAssignReverse(it + insertCount, remainSlice.slice(0, remainCount - growCount)); - Utils::copyAssign(it, insertSlice); - } - else { // remainSlice is moved beyond end() - Utils::moveConstruct(m_pointer + offset + insertCount, remainSlice); - auto assignElems = m_count - offset; - Utils::copyAssign(it, insertSlice.slice(0, assignElems)); - Utils::copyConstruct(storageEnd(), insertSlice.slice(assignElems, insertCount - assignElems)); - } - m_count = newCount; - } - - /// Same as splice but moves inserted elements into the array - void spliceMoved(Iterator it, Count removeCount, MoveSlice insertSlice) { - auto offset = it - amendBegin(); - auto insertCount = insertSlice.count(); - auto remainCount = m_count - offset - removeCount; - auto remainSlice = MoveSlice{it + removeCount, remainCount}; - // old: [ ..(offset)..,[it] ..(removeCount).., ..(remainCount)... ] - // new: [ ..(offset)..,[it] ..(insertCount).., ..(remainCount)... ] - - auto newCount = (m_count - removeCount) + insertCount; - if (m_capacity < newCount) { // not enough storage => arrange everything in new storage - auto newStorage = grownStorage(insertCount - removeCount); - Utils::moveConstruct(newStorage.begin(), MoveSlice{amendBegin(), static_cast(offset)}); - Utils::moveConstruct(newStorage.begin() + offset, insertSlice); - Utils::moveConstruct(newStorage.begin() + offset + insertCount, remainSlice); - Utils::destruct(amend()); - Utils::deallocate(Slice{m_pointer, m_capacity}); - m_pointer = newStorage.begin(); - m_capacity = newStorage.count(); - } - else if (m_count >= newCount) { // shrinking - auto shrinkCount = m_count - newCount; - Utils::moveAssign(it, insertSlice); - Utils::moveAssignForward(m_pointer + offset + insertCount, remainSlice); - Utils::destruct(Slice{amendEnd() - shrinkCount, shrinkCount}); - } - else if (offset + insertCount <= m_count) { // parts of remainSlice is moved beyond end() - auto growCount = newCount - m_count; - Utils::moveConstruct(storageEnd(), remainSlice.slice(remainCount - growCount, growCount)); - Utils::moveAssignReverse(it + insertCount, remainSlice.slice(0, remainCount - growCount)); - Utils::moveAssign(it, insertSlice); - } - else { // remainSlice is moved beyond end() - Utils::moveConstruct(m_pointer + offset + insertCount, remainSlice); - auto assignElems = m_count - offset; - Utils::moveAssign(it, insertSlice.slice(0, assignElems)); - Utils::moveConstruct(storageEnd(), insertSlice.slice(assignElems, insertCount - assignElems)); - } - m_count = newCount; - } - - /// Same as splice but moves inserted elements into the array - void remove(Iterator it, Count removeCount) { - auto offset = it - amendBegin(); - auto remainCount = m_count - offset - removeCount; - auto remainSlice = MoveSlice{it + removeCount, remainCount}; - - Utils::moveAssignForward(m_pointer + offset, remainSlice); - Utils::destruct(Slice{amendEnd() - removeCount, removeCount}); - m_count = m_count - removeCount; - } - -private: - [[nodiscard]] auto byteSize() const -> size_t { return m_count * sizeof(T); } - [[nodiscard]] auto storageEnd() -> Iterator { return m_pointer + m_count; } - - [[nodiscard]] auto grownStorage(size_t growBy) const -> Slice { - auto cur = m_capacity; - auto res = (cur << 1) - (cur >> 1) + (cur >> 4); // * 1.563 - if (res < 5) res = 5; - if (res < m_capacity + growBy) res = m_capacity + growBy; - auto ptr = Utils::allocate(res); - return Slice{ptr, res}; - } - void growBy(size_t by) { - auto newStorage = grownStorage(by); - Utils::moveConstruct(newStorage.begin(), move()); - Utils::destruct(amend()); - Utils::deallocate(Slice{m_pointer, m_capacity}); - m_pointer = newStorage.begin(); - m_capacity = newStorage.count(); - } -}; - -/// simplified deduction guide -/// usage: -/// DynamicArrayOf{1,2,3} -template DynamicArrayOf(T&&, Ts&&...) -> DynamicArrayOf; - -/// deduce T from slices -template DynamicArrayOf(SliceOf) -> DynamicArrayOf; -template DynamicArrayOf(MoveSliceOf) -> DynamicArrayOf; - -/// deduce SliceOf from DynamicArray -/// usage: -/// auto a = DynamicArrayOf{1,2,3; -/// auto slice = SliceOf{a}; -template SliceOf(const DynamicArrayOf&) -> SliceOf; - -} // namespace array19 diff --git a/src/array19.lib/array19/DynamicArrayOf.ostream.h b/src/array19.lib/array19/DynamicArrayOf.ostream.h deleted file mode 100644 index bdd6523e..00000000 --- a/src/array19.lib/array19/DynamicArrayOf.ostream.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once -#include "DynamicArrayOf.h" - -#include - -namespace array19 { - -template -auto operator<<(std::basic_ostream& out, const DynamicArrayOf& a) -> decltype(out)& { - out << "["; - bool first = true; - for (auto& v : a) { - if (first) - first = false; - else - out << ", "; - out << v; - } - return out << "]"; -} - -} // namespace array19 diff --git a/src/array19.lib/array19/DynamicSortedSet.h b/src/array19.lib/array19/DynamicSortedSet.h index 3d84c02f..9c579773 100644 --- a/src/array19.lib/array19/DynamicSortedSet.h +++ b/src/array19.lib/array19/DynamicSortedSet.h @@ -1,6 +1,6 @@ #pragma once -#include "DynamicArrayOf.h" -#include "SliceOf.single.h" +#include "DynamicArray.h" +#include "Span.one.h" #include #include // size_t @@ -21,14 +21,14 @@ template> struct DynamicSortedSet { } void add(T const& v) { - auto [b, e] = std::equal_range(m.amendBegin(), m.amendEnd(), v, Less{}); + auto [b, e] = std::equal_range(m.begin(), m.end(), v, Less{}); if (b == e) { - m.splice(b, 0, array19::sliceOfSingle(v)); + m.splice(b, 0, array19::spanOne(v)); } } void remove(T const& v) { - auto [b, e] = std::equal_range(m.amendBegin(), m.amendEnd(), v, Less{}); + auto [b, e] = std::equal_range(m.begin(), m.end(), v, Less{}); if (b != e) { m.remove(b, 1); } @@ -37,7 +37,7 @@ template> struct DynamicSortedSet { void clear() { m.clear(); } private: - DynamicArrayOf m; + DynamicArray m; }; } // namespace array19 diff --git a/src/array19.lib/array19/MoveSliceOf.h b/src/array19.lib/array19/MoveSliceOf.h deleted file mode 100644 index d86da420..00000000 --- a/src/array19.lib/array19/MoveSliceOf.h +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once -#include "SliceOf.h" - -#include // size_t - -namespace array19 { - -/// Same as SliceOf but this marks that elements want to be moved from -/// useful for initializers -template struct MoveSliceOf { - using Element = T; - using Count = size_t; - using Index = size_t; - -private: - Element* m_data{}; - Count m_count{}; - -public: - constexpr MoveSliceOf() = default; - constexpr explicit MoveSliceOf(Element* data, Count count) noexcept : m_data(data), m_count(count) {} - - [[nodiscard]] constexpr auto isEmpty() const noexcept -> bool { return m_count == 0; } - [[nodiscard]] constexpr auto count() const noexcept -> Count { return m_count; } - [[nodiscard]] constexpr auto slice(Index offset, Count count) const noexcept -> MoveSliceOf { - return MoveSliceOf{m_data + offset, count}; - } - - [[nodiscard]] constexpr auto begin() const noexcept -> Element* { return m_data; } - [[nodiscard]] constexpr auto end() const noexcept -> Element* { return m_data + m_count; } - [[nodiscard]] constexpr auto operator[](Index index) const noexcept -> Element&& { return *(m_data + index); } - - [[nodiscard]] constexpr operator SliceOf() const noexcept { return SliceOf{m_data, m_count}; } -}; - -template MoveSliceOf(T*, size_t) -> MoveSliceOf; - -} // namespace array19 diff --git a/src/array19.lib/array19/MoveSliceOf.single.h b/src/array19.lib/array19/MoveSliceOf.single.h deleted file mode 100644 index c2b9ccaa..00000000 --- a/src/array19.lib/array19/MoveSliceOf.single.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once -#include "MoveSliceOf.h" - -namespace array19 { - -/// view a single object as a move slice of one element -template static constexpr auto moveSliceOfSingle(T&& data) noexcept -> MoveSliceOf { - return MoveSliceOf{&data, 1}; -} - -} // namespace array19 diff --git a/src/array19.lib/array19/SliceOf.carray.h b/src/array19.lib/array19/SliceOf.carray.h deleted file mode 100644 index 009c2a04..00000000 --- a/src/array19.lib/array19/SliceOf.carray.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once -#include "SliceOf.h" - -#include // size_t -namespace array19 { - -template using CArray = T[N]; - -template constexpr auto sliceOfCArray(const T (&array)[N]) noexcept -> SliceOf { - return SliceOf{array, N}; -} - -template constexpr auto amendSliceOfCArray(CArray& array) noexcept -> SliceOf { - return SliceOf{array, N}; -} - -} // namespace array19 diff --git a/src/array19.lib/array19/SliceOf.equals.h b/src/array19.lib/array19/SliceOf.equals.h deleted file mode 100644 index e99775f7..00000000 --- a/src/array19.lib/array19/SliceOf.equals.h +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once -#include "SliceOf.h" - -#include // size_t - -namespace array19 { - -template constexpr bool operator==(const SliceOf& a, const SliceOf& b) { - if (a.count() != b.count()) return false; - for (size_t i = 0u; i < a.count() && i < b.count(); i++) { - if (!(a[i] == b[i])) return false; - } - return true; -} - -template constexpr bool operator!=(const SliceOf& a, const SliceOf& b) { return !(a == b); } - -} // namespace array19 diff --git a/src/array19.lib/array19/SliceOf.equals.test.cpp b/src/array19.lib/array19/SliceOf.equals.test.cpp deleted file mode 100644 index d05b034d..00000000 --- a/src/array19.lib/array19/SliceOf.equals.test.cpp +++ /dev/null @@ -1,39 +0,0 @@ -#include "SliceOf.carray.h" -#include "SliceOf.equals.h" - -using namespace array19; - -void constexpr_SliceOf_equals_test() { - constexpr static int a[3] = {1, 2, 3}; - constexpr auto as = sliceOfCArray(a); - - constexpr static int b[3] = {1, 2, 3}; - constexpr auto bs = amendSliceOfCArray(b); - - static_assert(as == bs); -} - -void constexpr_SliceOf_equals_empty_test() { - constexpr auto as = SliceOf{}; - constexpr auto bs = SliceOf{}; - static_assert(as == bs); -} - -void constexpr_SliceOf_equals_count_differ_test() { - constexpr static int a[3] = {1, 2, 3}; - constexpr auto as = sliceOfCArray(a); - - constexpr auto bs = SliceOf{}; - - static_assert(as != bs); -} - -void constexpr_SliceOf_equals_value_differ_test() { - constexpr static int a[3] = {1, 2, 3}; - constexpr auto as = sliceOfCArray(a); - - constexpr static int b[3] = {1, 99, 3}; - constexpr auto bs = amendSliceOfCArray(b); - - static_assert(as != bs); -} diff --git a/src/array19.lib/array19/SliceOf.h b/src/array19.lib/array19/SliceOf.h deleted file mode 100644 index 1c9d8980..00000000 --- a/src/array19.lib/array19/SliceOf.h +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once -#include // size_t - -namespace array19 { - -/// View on a slice of consequtive memory -/// Usage: -/// SliceOf // allow modification of values -/// SliceOf // values are treated const -/// -/// Note: Slice itself is an unmodifiable value object -template struct SliceOf { - using Element = T; - using Count = size_t; - using Index = size_t; - -private: - Element* m_data{}; - Count m_count{}; - -public: - constexpr SliceOf() = default; - constexpr explicit SliceOf(Element* data, Count count) noexcept : m_data(data), m_count(count) {} - - [[nodiscard]] constexpr auto isEmpty() const noexcept -> bool { return m_count == 0; } - [[nodiscard]] constexpr auto count() const noexcept -> Count { return m_count; } - [[nodiscard]] constexpr auto slice(Index offset, Count count) const noexcept -> SliceOf { - return SliceOf{m_data + offset, count}; - } - - [[nodiscard]] constexpr auto begin() const noexcept -> Element* { return m_data; } - [[nodiscard]] constexpr auto end() const noexcept -> Element* { return m_data + m_count; } - [[nodiscard]] constexpr auto operator[](Index index) const noexcept -> Element& { return *(m_data + index); } - - [[nodiscard]] constexpr operator SliceOf() const noexcept { return SliceOf{m_data, m_count}; } -}; - -template struct SliceOf; // use MoveSliceOf for that - -template SliceOf(T*, size_t) -> SliceOf; - -} // namespace array19 diff --git a/src/array19.lib/array19/SliceOf.single.h b/src/array19.lib/array19/SliceOf.single.h deleted file mode 100644 index 06baaa06..00000000 --- a/src/array19.lib/array19/SliceOf.single.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once -#include "SliceOf.h" - -namespace array19 { - -/// view a single object as a slice of one element -template static constexpr auto sliceOfSingle(T& data) noexcept -> SliceOf { return SliceOf{&data, 1}; } - -} // namespace array19 diff --git a/src/array19.lib/array19/SliceOf.store.h b/src/array19.lib/array19/SliceOf.store.h deleted file mode 100644 index 2cda14a6..00000000 --- a/src/array19.lib/array19/SliceOf.store.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once -#include "Array.h" -#include "SliceOf.h" - -#include // size_t -#include - -namespace array19 { - -/// note: -/// You have to know the size at compile time to create an Array! -/// usage: -/// constexpr auto slice = SliceOf...; -/// constexpr auto array = storeSlice(slice); -template constexpr auto storeSlice(const SliceOf& slice) noexcept -> Array { - return [&](std::index_sequence*)->Array { return {slice[Is]...}; } - (static_cast*>(nullptr)); -} - -} // namespace array19 diff --git a/src/array19.lib/array19/Span.carray.h b/src/array19.lib/array19/Span.carray.h new file mode 100644 index 00000000..bfc5935e --- /dev/null +++ b/src/array19.lib/array19/Span.carray.h @@ -0,0 +1,20 @@ +#pragma once +#include "Span.h" + +#include // size_t +namespace array19 { + +template using CArray = T[N]; + +template constexpr auto spanOfCArray(CArray const& array) noexcept -> Span { + return Span{array, N}; +} + +template constexpr auto amendSpanOfCArray(CArray& array) noexcept -> Span { + return Span{array, N}; +} +template constexpr auto moveSpanOfCArray(CArray&& array) noexcept -> Span { + return Span{array, N}; +} + +} // namespace array19 diff --git a/src/array19.lib/array19/Span.equals.h b/src/array19.lib/array19/Span.equals.h new file mode 100644 index 00000000..3dd78448 --- /dev/null +++ b/src/array19.lib/array19/Span.equals.h @@ -0,0 +1,21 @@ +#pragma once +#include "Span.h" + +#include // size_t + +namespace array19 { + +template constexpr bool operator==(Span const& a, Span const& b) { + if (a.count() != b.count()) { + return false; + } + for (auto i = size_t{}; i < a.count() && i < b.count(); i++) { + if (a[i] != b[i]) return false; + } + return true; +} + +// generated automatilly now +// template constexpr bool operator!=(Span const& a, Span const& b) { return !(a == b); } + +} // namespace array19 diff --git a/src/array19.lib/array19/Span.equals.test.cpp b/src/array19.lib/array19/Span.equals.test.cpp new file mode 100644 index 00000000..cbdc7177 --- /dev/null +++ b/src/array19.lib/array19/Span.equals.test.cpp @@ -0,0 +1,39 @@ +#include "Span.carray.h" +#include "Span.equals.h" + +using namespace array19; + +void constexpr_Span_equals_test() { + constexpr static int a[3] = {1, 2, 3}; + constexpr auto as = spanOfCArray(a); + + constexpr static int b[3] = {1, 2, 3}; + constexpr auto bs = amendSpanOfCArray(b); + + static_assert(as == bs); +} + +void constexpr_Span_equals_empty_test() { + constexpr auto as = Span{}; + constexpr auto bs = Span{}; + static_assert(as == bs); +} + +void constexpr_Span_equals_count_differ_test() { + constexpr static int a[3] = {1, 2, 3}; + constexpr auto as = spanOfCArray(a); + + constexpr auto bs = Span{}; + + static_assert(as != bs); +} + +void constexpr_Span_equals_value_differ_test() { + constexpr static int a[3] = {1, 2, 3}; + constexpr auto as = spanOfCArray(a); + + constexpr static int b[3] = {1, 99, 3}; + constexpr auto bs = amendSpanOfCArray(b); + + static_assert(as != bs); +} diff --git a/src/array19.lib/array19/Span.h b/src/array19.lib/array19/Span.h new file mode 100644 index 00000000..e847c56e --- /dev/null +++ b/src/array19.lib/array19/Span.h @@ -0,0 +1,74 @@ +#pragma once +#include // size_t +#include // std::remove_reference_t +#include // std::move + +namespace array19 { + +/// View on a span of consequtive memory +/// Usage: +/// Span // allow modification of values +/// Span // values are treated const +/// Span // mark for move +/// +/// Note: Span itself is an unmodifiable value object +template struct Span { + using Data = std::remove_reference_t; + using Count = size_t; + using Index = size_t; + static constexpr auto is_move = std::is_rvalue_reference_v; + +private: + Data* m_data{}; + Count m_count{}; + +public: + constexpr Span() = default; + constexpr explicit Span(Data* data, Count count) noexcept : m_data{data}, m_count{count} {} + + [[nodiscard]] constexpr auto isEmpty() const noexcept -> bool { return m_count == 0; } + [[nodiscard]] constexpr auto count() const noexcept -> Count { return m_count; } + + [[nodiscard]] constexpr auto begin() const noexcept -> Data* { return m_data; } + [[nodiscard]] constexpr auto end() const noexcept -> Data* { return m_data + m_count; } + // requires: index < m_count + [[nodiscard]] constexpr auto operator[](Index index) const noexcept -> Data& requires(!is_move) { + return *(m_data + index); + } + [[nodiscard]] constexpr auto operator[](Index index) const noexcept -> Data requires(is_move) { + return std::move(*(m_data + index)); + } + + // allow use of mutable/move span in places where const is expected + [[nodiscard]] constexpr operator Span() const noexcept { return Span{m_data, m_count}; } + + // requires: offset + count <= m_count + [[nodiscard]] constexpr auto slice(Index offset, Count count) const noexcept -> Span { + return Span{m_data + offset, count}; + } + // requires: offset <= m_count + [[nodiscard]] constexpr auto skip(Index offset) const noexcept -> Span { + return Span{m_data + offset, static_cast(m_count - offset)}; + } + // requires: count <= m_count + [[nodiscard]] constexpr auto head(Count count) const noexcept -> Span { + return Span{m_data, count}; // + } + // requires: count <= m_count + [[nodiscard]] constexpr auto tail(Count count) const noexcept -> Span { + return Span{m_data + m_count - count, count}; + } + // requires: count <= m_count + [[nodiscard]] constexpr auto drop_tail(Count count) const noexcept -> Span { + return Span{m_data, static_cast(m_count - count)}; + } +}; + +template struct Span; // wont work - use Span +template struct Span; // that's nonsense + +template Span(T*, size_t) -> Span; + +template constexpr auto moveSpan(T* data, size_t count) -> Span { return Span{data, count}; } + +} // namespace array19 diff --git a/src/array19.lib/array19/SliceOf.max.h b/src/array19.lib/array19/Span.max.h similarity index 52% rename from src/array19.lib/array19/SliceOf.max.h rename to src/array19.lib/array19/Span.max.h index b8029243..e0785372 100644 --- a/src/array19.lib/array19/SliceOf.max.h +++ b/src/array19.lib/array19/Span.max.h @@ -1,12 +1,13 @@ #pragma once -#include "SliceOf.h" +#include "Span.h" namespace array19 { -template constexpr auto sliceMaximum(SliceOf slice) { +template constexpr auto spanMaximum(Span span) { auto result = T{}; - for (auto v : slice) + for (auto v : span) { if (v > result) result = v; + } return result; } diff --git a/src/array19.lib/array19/Span.one.h b/src/array19.lib/array19/Span.one.h new file mode 100644 index 00000000..91394c7a --- /dev/null +++ b/src/array19.lib/array19/Span.one.h @@ -0,0 +1,16 @@ +#pragma once +#include "Span.h" + +namespace array19 { + +/// view a single object as a span of one element +template static constexpr auto spanOne(T const& data) noexcept -> Span { return Span{&data, 1}; } + +template static constexpr auto amendSpanOne(T& data) noexcept -> Span { return Span{&data, 1}; } + +template +static constexpr auto moveSpanOne(T&& data) noexcept -> Span requires(std::is_rvalue_reference_v) { + return Span{&data, 1}; +} + +} // namespace array19 diff --git a/src/array19.lib/array19/SliceOf.ostream.h b/src/array19.lib/array19/Span.ostream.h similarity index 73% rename from src/array19.lib/array19/SliceOf.ostream.h rename to src/array19.lib/array19/Span.ostream.h index d822af73..25c30da3 100644 --- a/src/array19.lib/array19/SliceOf.ostream.h +++ b/src/array19.lib/array19/Span.ostream.h @@ -1,12 +1,12 @@ #pragma once -#include "SliceOf.h" +#include "Span.h" #include namespace array19 { template -auto operator<<(std::basic_ostream& out, const SliceOf& s) -> decltype(out)& { +auto operator<<(std::basic_ostream& out, Span const& s) -> decltype(out)& { out << "["; bool first = true; for (auto& v : s) { diff --git a/src/array19.lib/array19/Span.store.h b/src/array19.lib/array19/Span.store.h new file mode 100644 index 00000000..3aa85262 --- /dev/null +++ b/src/array19.lib/array19/Span.store.h @@ -0,0 +1,22 @@ +#pragma once +#include "Array.h" +#include "Span.h" + +#include // size_t +#include // std::index_sequence + +namespace array19 { + +/// note: +/// You have to know the size at compile time to create an Array! +/// usage: +/// constexpr auto span = Span{...}; +/// constexpr auto array = storeSpan(span); +template::Data> +constexpr auto storeSpan(Span const& span) noexcept -> Array { + // note: using index_sequence allows constructing arrays of const elements, also takes care of move + return [&](std::index_sequence*)->Array { return {span[Is]...}; } + (static_cast*>(nullptr)); +} + +} // namespace array19 diff --git a/src/array19.lib/array19/SliceOf.test.cpp b/src/array19.lib/array19/Span.test.cpp similarity index 63% rename from src/array19.lib/array19/SliceOf.test.cpp rename to src/array19.lib/array19/Span.test.cpp index 3fc9edba..5add9674 100644 --- a/src/array19.lib/array19/SliceOf.test.cpp +++ b/src/array19.lib/array19/Span.test.cpp @@ -1,15 +1,15 @@ -#include "SliceOf.h" +#include "Span.h" -#include "SliceOf.carray.h" -#include "SliceOf.store.h" +#include "Span.carray.h" +#include "Span.store.h" #include using namespace array19; -void constexpr_SliceOf_test() { +void constexpr_Span_test() { constexpr static int a[3] = {1, 2, 3}; - constexpr auto s = sliceOfCArray(a); + constexpr auto s = spanOfCArray(a); static_assert(s.count() == 3); static_assert(s[1] == 2); @@ -22,9 +22,9 @@ void constexpr_SliceOf_test() { static_assert(sum == 6); } -TEST(SliceOf, Access) { +TEST(Span, Access) { int a[3] = {1, 2, 3}; - auto s = sliceOfCArray(a); + auto s = spanOfCArray(a); EXPECT_EQ(s.count(), 3u); EXPECT_EQ(s[1], 2); @@ -34,24 +34,24 @@ TEST(SliceOf, Access) { EXPECT_EQ(sum, 6); } -void mutable_SliceOf_test() { +void mutable_Span_test() { constexpr auto sa = [] { int a[] = {1, 2, 3}; - auto s = amendSliceOfCArray(a); + auto s = amendSpanOfCArray(a); s[1] = 5; - return storeSlice<3>(s); + return storeSpan<3>(s); }(); static_assert(sa[1] == 5); } -TEST(SliceOf, Mutate) { +TEST(Span, Mutate) { int a[] = {1, 2, 3, 4}; - auto s = amendSliceOfCArray(a); + auto s = amendSpanOfCArray(a); s[1] = 5; EXPECT_EQ(a[1], 5); - auto sa = storeSlice<4>(s); + auto sa = storeSpan<4>(s); EXPECT_EQ(sa[1], 5); EXPECT_EQ(sa[3], 4); } diff --git a/src/array19.lib/array19/WithIndex.h b/src/array19.lib/array19/WithIndex.h index bd924eea..6e01dbbf 100644 --- a/src/array19.lib/array19/WithIndex.h +++ b/src/array19.lib/array19/WithIndex.h @@ -9,8 +9,8 @@ namespace array19 { /// usage: /// for(auto [value, index] : WithIndex{container}); template struct WithIndex { - using It = decltype(adlBegin(*static_cast(nullptr))); - using Res = decltype(**static_cast(nullptr)); + using It = decltype(adlBegin(std::declval())); + using Res = decltype(*std::declval()); // not using std::pair to avoid reference hassles struct result { @@ -28,15 +28,15 @@ template struct WithIndex { constexpr auto operator++() -> iterator& { return (++it, ++index, *this); } }; - constexpr WithIndex(C& c) noexcept : c(c) {} + constexpr WithIndex(C&& c) noexcept : c((C &&) c) {} [[nodiscard]] constexpr auto begin() -> iterator { return iterator{adlBegin(c), {}}; } [[nodiscard]] constexpr auto end() -> It { return adlEnd(c); } private: - C& c; + C&& c; }; -template WithIndex(C&) -> WithIndex; +template WithIndex(C&&) -> WithIndex; } // namespace array19 diff --git a/src/array19.lib/array19/WithIndex.test.cpp b/src/array19.lib/array19/WithIndex.test.cpp index 5c54dde9..b8589db5 100644 --- a/src/array19.lib/array19/WithIndex.test.cpp +++ b/src/array19.lib/array19/WithIndex.test.cpp @@ -6,12 +6,7 @@ void constexpr_WithIndex_test() { constexpr auto sum = [] { int a[] = {1, 2, 3}; auto r = 0; -#ifdef _MSC_VER - auto wi = WithIndex{a}; // cl crashes if we inline this && run it as constexpr - for (auto [v, i] : wi) { -#else for (auto [v, i] : WithIndex{a}) { -#endif r += v * i; } return r; diff --git a/src/array19.lib/array19/Zip.test.cpp b/src/array19.lib/array19/Zip.test.cpp index 4f76c7b6..0bccf6d6 100644 --- a/src/array19.lib/array19/Zip.test.cpp +++ b/src/array19.lib/array19/Zip.test.cpp @@ -9,12 +9,7 @@ void constexpr_Zip_test() { auto a = Array{1, 2, 3}; unsigned b[] = {4, 5, 6}; auto r = 0; -#ifdef _MSC_VER - auto z = Zip{a, b}; // cl crashes if we inline this && run it as constexpr - for (auto [va, vb] : z) { -#else for (auto [va, vb] : Zip{a, b}) { -#endif r += va * vb; } return r; diff --git a/src/array19.lib/array19/array19.qbs b/src/array19.lib/array19/array19.qbs index 1c20c0be..2a10e6db 100644 --- a/src/array19.lib/array19/array19.qbs +++ b/src/array19.lib/array19/array19.qbs @@ -9,25 +9,23 @@ Product { } files: [ - "AllocatedArrayOf.equals.h", - "AllocatedArrayOf.h", - "AllocatedArrayOf.ostream.h", + "AllocatedArray.equals.h", + "AllocatedArray.h", + "AllocatedArray.ostream.h", "AllocatedArrayUtils.h", "Array.h", "Array.ostream.h", - "DynamicArrayOf.equals.h", - "DynamicArrayOf.h", - "DynamicArrayOf.ostream.h", + "DynamicArray.equals.h", + "DynamicArray.h", + "DynamicArray.ostream.h", "DynamicSortedSet.h", - "MoveSliceOf.h", - "MoveSliceOf.single.h", - "SliceOf.carray.h", - "SliceOf.equals.h", - "SliceOf.h", - "SliceOf.max.h", - "SliceOf.ostream.h", - "SliceOf.single.h", - "SliceOf.store.h", + "Span.carray.h", + "Span.equals.h", + "Span.h", + "Span.max.h", + "Span.one.h", + "Span.ostream.h", + "Span.store.h", "WithIndex.h", "Zip.h", "adlRange.h", diff --git a/src/lookup19.lib/lookup19/OrderedMapOf.equals.h b/src/lookup19.lib/lookup19/OrderedMapArray.equals.h similarity index 50% rename from src/lookup19.lib/lookup19/OrderedMapOf.equals.h rename to src/lookup19.lib/lookup19/OrderedMapArray.equals.h index 08aeee42..9b3f0100 100644 --- a/src/lookup19.lib/lookup19/OrderedMapOf.equals.h +++ b/src/lookup19.lib/lookup19/OrderedMapArray.equals.h @@ -1,11 +1,11 @@ #pragma once -#include "OrderedMapOf.h" -#include "array19/SliceOf.equals.h" +#include "OrderedMapArray.h" +#include "array19/Span.equals.h" namespace lookup19 { template -bool operator==(const OrderedMapOf& a, const OrderedMapOf& b) { +bool operator==(const OrderedMapArray& a, const OrderedMapArray& b) { return a.keys() == b.keys() && a.values() == b.values(); } diff --git a/src/lookup19.lib/lookup19/OrderedMapOf.h b/src/lookup19.lib/lookup19/OrderedMapArray.h similarity index 54% rename from src/lookup19.lib/lookup19/OrderedMapOf.h rename to src/lookup19.lib/lookup19/OrderedMapArray.h index 52844906..675f3238 100644 --- a/src/lookup19.lib/lookup19/OrderedMapOf.h +++ b/src/lookup19.lib/lookup19/OrderedMapArray.h @@ -1,11 +1,12 @@ #pragma once -#include "OrderedSliceOf.h" +#include "OrderedSpan.h" #include "array19/AllocatedArrayUtils.h" -#include "array19/MoveSliceOf.h" +#include "array19/Span.one.h" -namespace lookup19 { +#include // uintptr_t +#include // std::bit_cast -using array19::MoveSliceOf; +namespace lookup19 { struct DefaultLess { template constexpr auto operator()(const A& a, const B& b) const { return a < b; } @@ -16,18 +17,23 @@ struct DefaultLess { /// * avoids pointer chasing /// * addresses are not stable on insert or remove /// * prefer to store value pointers for big objects to make efficient -template struct OrderedMapOf { +template struct OrderedMapArray { + using Key = K; + using Value = V; + using Less = L; using Count = size_t; using Index = size_t; - using KeySlice = SliceOf; - using KeyOrderedSlice = OrderedSliceOf; - using KeyMoveSlice = MoveSliceOf; - using KeyAmendSlice = SliceOf; + using KeyConstSpan = Span; + using KeyAmendSpan = Span; + using KeyMoveSpan = Span; + using KeyConstOrderedSpan = OrderedSpan; + using KeyAmendOrderedSpan = OrderedSpan; + using KeyMoveOrderedSpan = OrderedSpan; - using ValueSlice = SliceOf; - using ValueAmendSlice = SliceOf; - using ValueMoveSlice = MoveSliceOf; + using ValueConstSpan = Span; + using ValueAmendSpan = Span; + using ValueMoveSpan = Span; private: using KeyUtils = array19::AllocatedArrayUtils; @@ -36,83 +42,95 @@ template struct OrderedMapOf { static constexpr auto value_align_offset = alignof(Key) < alignof(Value) ? alignof(Value) - alignof(Key) : 0; static constexpr auto value_align_mask = ~static_cast(alignof(Value) - 1); - Key* m_pointer{}; + Key* m_keyData{}; Count m_count{}; Count m_capacity{}; - [[nodiscard]] auto valuesPointer() -> Value* { return toValuesPointer(m_pointer + m_capacity); } - [[nodiscard]] auto valuesPointer() const -> Value const* { return toValuesPointer(m_pointer + m_capacity); } + [[nodiscard]] auto _valueData() -> Value* { return _toValuesPointer(m_keyData + m_capacity); } + [[nodiscard]] auto _valueData() const -> Value const* { return _toValuesPointer(m_keyData + m_capacity); } - [[nodiscard]] auto keyData() const noexcept -> Key const* { - return std::launder(reinterpret_cast(m_pointer)); + [[nodiscard]] auto _keyConstBegin() const noexcept -> Key const* { + return std::launder(reinterpret_cast(m_keyData)); } - [[nodiscard]] auto valueData() noexcept -> Value* { - return std::launder(reinterpret_cast(valuesPointer())); + [[nodiscard]] auto _keyAmendBegin() const noexcept -> Key* { + return std::launder(reinterpret_cast(m_keyData)); } - [[nodiscard]] auto valueData() const noexcept -> Value const* { - return std::launder(reinterpret_cast(valuesPointer())); + [[nodiscard]] auto _keyAmend() const noexcept -> KeyAmendSpan { return KeyAmendSpan{_keyAmendBegin(), m_count}; } + [[nodiscard]] auto _valueAmendBegin() noexcept -> Value* { + return std::launder(reinterpret_cast(_valueData())); } + [[nodiscard]] auto _valueConstBegin() const noexcept -> Value const* { + return std::launder(reinterpret_cast(_valueData())); + } + [[nodiscard]] auto _amendCapacity() const noexcept -> KeyAmendSpan { return KeyAmendSpan{m_keyData, m_capacity}; } public: - OrderedMapOf() = default; - ~OrderedMapOf() noexcept { - if (m_pointer) { - KeyUtils::destruct(KeyAmendSlice{m_pointer, m_count}); - ValueUtils::destruct(ValueAmendSlice{valuesPointer(), m_count}); - OrderedMapOf::deallocate(KeyAmendSlice{m_pointer, m_capacity}); + OrderedMapArray() = default; + ~OrderedMapArray() noexcept { + if (m_keyData) { + KeyUtils::destruct(_keyAmend()); + ValueUtils::destruct(amendValues()); + OrderedMapArray::deallocate(_amendCapacity()); } } - explicit OrderedMapOf(KeyOrderedSlice newKeys, Value const* newValues) - : m_pointer{OrderedMapOf::allocate(newKeys.count())} + explicit OrderedMapArray(KeyConstOrderedSpan newKeys, Value const* newValues) + : m_keyData{OrderedMapArray::allocate(newKeys.count())} + , m_count{newKeys.count()} + , m_capacity{newKeys.count()} { + KeyUtils::copyConstruct(m_keyData, newKeys); + ValueUtils::copyConstruct(_valueData(), ValueConstSpan{newValues, newKeys.count()}); + } + explicit OrderedMapArray(KeyMoveOrderedSpan newKeys, Value* newValues) + : m_keyData{OrderedMapArray::allocate(newKeys.count())} , m_count{newKeys.count()} , m_capacity{newKeys.count()} { - KeyUtils::copyConstruct(m_pointer, newKeys); - ValueUtils::copyConstruct(valuesPointer(), ValueSlice{newValues, newKeys.count()}); + KeyUtils::moveConstruct(m_keyData, newKeys); + ValueUtils::moveConstruct(_valueData(), ValueMoveSpan{newValues, newKeys.count()}); } - OrderedMapOf(const OrderedMapOf& o) - : m_pointer{OrderedMapOf::allocate(o.m_count)} + OrderedMapArray(OrderedMapArray const& o) + : m_keyData{OrderedMapArray::allocate(o.m_count)} , m_count(o.m_count) , m_capacity(o.m_count) { - KeyUtils::copyConstruct(m_pointer, o.keys()); - ValueUtils::copyConstruct(valuesPointer(), o.values()); + KeyUtils::copyConstruct(m_keyData, o.keys()); + ValueUtils::copyConstruct(_valueData(), o.values()); } - OrderedMapOf& operator=(const OrderedMapOf& o) { - if (!m_pointer || m_capacity < o.m_count) { - *this = OrderedMapOf{o}; + OrderedMapArray& operator=(OrderedMapArray const& o) { + if (!m_keyData || m_capacity < o.m_count) { + *this = OrderedMapArray{o}; } else { if (m_count > o.m_count) { - KeyUtils::copyAssign(m_pointer, o.keys()); - ValueUtils::copyAssign(valuesPointer(), o.values()); - KeyUtils::destruct(KeyAmendSlice{m_pointer + o.m_count, m_count - o.m_count}); - ValueUtils::destruct(ValueAmendSlice{valuesPointer() + o.m_count, m_count - o.m_count}); + KeyUtils::copyAssign(m_keyData, o.keys()); + ValueUtils::copyAssign(_valueData(), o.values()); + KeyUtils::destruct(_keyAmend().skip(o.m_count)); + ValueUtils::destruct(amendValues().skip(o.m_count)); } else { - KeyUtils::copyAssign(m_pointer, KeySlice{o.keyData(), m_count}); - ValueUtils::copyAssign(valuesPointer(), ValueSlice{o.valueData(), m_count}); - KeyUtils::copyConstruct(m_pointer + m_count, KeySlice{o.keyData() + m_count, o.m_count - m_count}); - ValueUtils::copyConstruct( - valuesPointer() + m_count, - ValueSlice{o.valueData() + m_count, o.m_count - m_count}); + KeyUtils::copyAssign(m_keyData, o.keys().head(m_count)); + ValueUtils::copyAssign(_valueData(), o.values().head(m_count)); + KeyUtils::copyConstruct(m_keyData + m_count, o.keys().skip(m_count)); + ValueUtils::copyConstruct(_valueData() + m_count, o.values().skip(m_count)); } m_count = o.m_count; } return *this; } - OrderedMapOf(OrderedMapOf&& o) noexcept : m_pointer(o.m_pointer), m_count(o.m_count), m_capacity(o.m_capacity) { - o.m_pointer = nullptr; - } - OrderedMapOf& operator=(OrderedMapOf&& o) noexcept { - if (m_pointer) { - return *this = OrderedMapOf{std::move(o)}; + OrderedMapArray(OrderedMapArray&& o) noexcept + : m_keyData(std::exchange(o.m_keyData, nullptr)) + , m_count(o.m_count) + , m_capacity(o.m_capacity) {} + OrderedMapArray& operator=(OrderedMapArray&& o) noexcept { + if (m_keyData) { + KeyUtils::destruct(_keyAmend()); + ValueUtils::destruct(amendValues()); + OrderedMapArray::deallocate(_amendCapacity()); } - m_pointer = o.m_pointer; + m_keyData = std::exchange(o.m_keyData, nullptr); m_count = o.m_count; m_capacity = o.m_capacity; - o.m_pointer = nullptr; return *this; } @@ -121,14 +139,16 @@ template struct OrderedMapOf { [[nodiscard]] auto totalCapacity() const -> Count { return m_capacity; } [[nodiscard]] auto unusedCapacity() const -> Count { return m_capacity - m_count; } - [[nodiscard]] auto keys() const noexcept -> KeySlice { return KeySlice{keyData(), m_count}; } - [[nodiscard]] auto orderedKeys() const noexcept -> KeyOrderedSlice { return KeyOrderedSlice{keyData(), m_count}; } + [[nodiscard]] auto keys() const noexcept -> KeyConstSpan { return KeyConstSpan{_keyConstBegin(), m_count}; } + [[nodiscard]] auto orderedKeys() const noexcept -> KeyConstOrderedSpan { + return KeyConstOrderedSpan{_keyConstBegin(), m_count}; + } // note: amendKeys is omitted, to ensure order of elements is preserved! - [[nodiscard]] auto moveKeys() noexcept -> KeyMoveSlice { return KeyMoveSlice{m_pointer, m_count}; } + [[nodiscard]] auto moveKeys() noexcept -> KeyMoveSpan { return KeyMoveSpan{_keyAmendBegin(), m_count}; } - [[nodiscard]] auto values() const noexcept -> ValueSlice { return ValueSlice{valueData(), m_count}; } - [[nodiscard]] auto amendValues() noexcept -> ValueAmendSlice { return ValueAmendSlice{valueData(), m_count}; } - [[nodiscard]] auto moveValues() noexcept -> ValueMoveSlice { return ValueMoveSlice{valueData(), m_count}; } + [[nodiscard]] auto values() const noexcept -> ValueConstSpan { return ValueConstSpan{_valueConstBegin(), m_count}; } + [[nodiscard]] auto amendValues() noexcept -> ValueAmendSpan { return ValueAmendSpan{_valueAmendBegin(), m_count}; } + [[nodiscard]] auto moveValues() noexcept -> ValueMoveSpan { return ValueMoveSpan{_valueAmendBegin(), m_count}; } void ensureCapacity(Count count) { if (totalCapacity() < count) growBy(count - totalCapacity()); @@ -145,14 +165,14 @@ template struct OrderedMapOf { [[nodiscard]] auto findBy(Key const& key) const noexcept -> Value const* { auto keyRange = orderedKeys().equalRange(key); if (keyRange.isEmpty()) return nullptr; - auto index = static_cast(keyRange.begin() - m_pointer); - return valueData() + index; + auto index = static_cast(keyRange.begin() - m_keyData); + return _valueConstBegin() + index; } [[nodiscard]] auto amendBy(Key const& key) noexcept -> Value* { auto keyRange = orderedKeys().equalRange(key); if (keyRange.isEmpty()) return nullptr; - auto index = static_cast(keyRange.begin() - m_pointer); - return valueData() + index; + auto index = static_cast(keyRange.begin() - m_keyData); + return _valueAmendBegin() + index; } /// emplace a single value to the map if key is not present @@ -167,24 +187,24 @@ template struct OrderedMapOf { auto insert(Key key, Value value) -> Value* { auto valueIt = add(std::move(key)); if (valueIt == nullptr) return nullptr; - ValueUtils::moveConstruct(valueIt, ValueMoveSlice{&value, 1}); + ValueUtils::moveConstruct(valueIt, ValueMoveSpan{&value, 1}); return valueIt; } - void mergeRange(KeyOrderedSlice newKeys, Value const* newValues) { + void mergeRange(KeyConstOrderedSpan newKeys, Value const* newValues) { if (newKeys.isEmpty()) return; if (isEmpty()) { - *this = OrderedMapOf{newKeys, newValues}; + *this = OrderedMapArray{newKeys, newValues}; return; } auto const totalCount = newKeys.count() + m_count; if (totalCount > m_capacity) { auto newStorage = grownStorage(newKeys.count()); auto count = mergeConstructInto(newStorage, newKeys, newValues); - KeyUtils::destruct(KeyAmendSlice{m_pointer, m_count}); - ValueUtils::destruct(ValueAmendSlice{valuesPointer(), m_count}); - deallocate(KeyAmendSlice{m_pointer, m_capacity}); - m_pointer = newStorage.begin(); + KeyUtils::destruct(KeyAmendSpan{m_keyData, m_count}); + ValueUtils::destruct(ValueAmendSpan{_valueData(), m_count}); + deallocate(KeyAmendSpan{m_keyData, m_capacity}); + m_keyData = newStorage.begin(); m_count = count; m_capacity = newStorage.count(); } @@ -192,16 +212,24 @@ template struct OrderedMapOf { m_count = inplaceMerge(newKeys, newValues); } } + void mergeMoveRange(KeyMoveOrderedSpan newKeys, Value* newValues) { + if (newKeys.isEmpty()) return; + if (isEmpty()) { + *this = OrderedMapArray{newKeys, newValues}; + return; + } + // TODO + } void remove(Key const* cIt) { auto keyIt = const_cast(cIt); - auto index = static_cast(keyIt - m_pointer); - auto valueIt = valuesPointer() + index; - KeyUtils::destruct(KeyAmendSlice{keyIt, 1}); - ValueUtils::destruct(ValueAmendSlice{valueIt, 1}); + auto index = static_cast(keyIt - m_keyData); + auto valueIt = _valueData() + index; + KeyUtils::destruct(KeyAmendSpan{keyIt, 1}); + ValueUtils::destruct(ValueAmendSpan{valueIt, 1}); if (index + 1 != m_count) { - KeyUtils::moveAssignReverse(keyIt, KeyMoveSlice{keyIt + 1, m_count - index - 1}); - ValueUtils::moveAssignReverse(valueIt, ValueMoveSlice{valueIt + 1, m_count - index - 1}); + KeyUtils::moveAssignReverse(keyIt, moveKeys().skip(index + 1)); + ValueUtils::moveAssignReverse(valueIt, moveValues().skip(index + 1)); } m_count--; } @@ -219,51 +247,51 @@ template struct OrderedMapOf { return reinterpret_cast(::operator new[](size)); } } - static auto deallocate(KeyAmendSlice slice) { + static auto deallocate(KeyAmendSpan span) { #if __cpp_sized_deallocation - auto key_size = sizeof(Key) * slice.count(); + auto key_size = sizeof(Key) * span.count(); auto aligned_size = (key_size + value_align_offset) & value_align_mask; - auto size = aligned_size + sizeof(Value) * slice.count(); + auto size = aligned_size + sizeof(Value) * span.count(); if constexpr (__STDCPP_DEFAULT_NEW_ALIGNMENT__ < alignof(Key)) { - ::operator delete[](slice.begin(), size, std::align_val_t{alignof(Key)}); + ::operator delete[](span.begin(), size, std::align_val_t{alignof(Key)}); } else { - ::operator delete[](slice.begin(), size); + ::operator delete[](span.begin(), size); } #else if constexpr (__STDCPP_DEFAULT_NEW_ALIGNMENT__ < alignof(Key)) { - ::operator delete[](slice.begin(), std::align_val_t{alignof(Key)}); + ::operator delete[](span.begin(), std::align_val_t{alignof(Key)}); } else { - ::operator delete[](slice.begin()); + ::operator delete[](span.begin()); } #endif } template, Value const, Value>> - static auto toValuesPointer(Key* ptr) -> Value* { + static auto _toValuesPointer(Key* ptr) -> Value* { auto aligned_address = (std::bit_cast(ptr) + value_align_offset) & value_align_mask; return std::bit_cast(aligned_address); } - auto keyToValue(Key const* key) -> Value& { return valuesPointer()[key - m_pointer]; } - auto keyToValue(Key const* key) const -> Value const& { return valuesPointer()[key - m_pointer]; } + auto keyToValue(Key const* key) -> Value& { return _valueData()[key - m_keyData]; } + auto keyToValue(Key const* key) const -> Value const& { return _valueData()[key - m_keyData]; } - [[nodiscard]] auto grownStorage(int growBy) const -> KeyAmendSlice { + [[nodiscard]] auto grownStorage(int growBy) const -> KeyAmendSpan { auto cur = m_capacity; auto res = (cur << 1) - (cur >> 1) + (cur >> 4); // * 1.563 if (res < 5) res = 5; if (res < m_capacity + growBy) res = m_capacity + growBy; - auto ptr = OrderedMapOf::allocate(res); - return KeyAmendSlice{ptr, res}; + auto ptr = OrderedMapArray::allocate(res); + return KeyAmendSpan{ptr, res}; } void growBy(int by) { auto newStorage = grownStorage(by); KeyUtils::moveConstruct(newStorage.begin(), moveKeys()); - ValueUtils::moveConstruct(toValuesPointer(newStorage.end()), moveValues()); - KeyUtils::destruct(KeyAmendSlice{m_pointer, m_count}); + ValueUtils::moveConstruct(_toValuesPointer(newStorage.end()), moveValues()); + KeyUtils::destruct(_keyAmend()); ValueUtils::destruct(amendValues()); - OrderedMapOf::deallocate(SliceOf{m_pointer, m_capacity}); - m_pointer = newStorage.begin(); + OrderedMapArray::deallocate(_amendCapacity()); + m_keyData = newStorage.begin(); m_capacity = newStorage.count(); } @@ -274,54 +302,56 @@ template struct OrderedMapOf { return addToCapacity(key); } auto addToCapacity(Key key) -> Value* { - auto keyIt = const_cast(orderedKeys().lowerBound(key)); - if (keyIt != orderedKeys().end() && *keyIt == key) { + auto keyRange = orderedKeys().equalRange(key); + if (!keyRange.isEmpty()) { return nullptr; } - auto index = static_cast(keyIt - m_pointer); - auto valueIt = valuesPointer() + index; + auto keyIt = const_cast(keyRange.begin()); + auto index = static_cast(keyIt - m_keyData); + auto valueIt = _valueData() + index; if (index != m_count) { - KeyUtils::moveAssignForward(keyIt + 1, KeyMoveSlice{keyIt, m_count - index}); - ValueUtils::moveAssignForward(valueIt + 1, ValueMoveSlice{valueIt, m_count - index}); + KeyUtils::moveAssignForward(keyIt + 1, moveKeys().skip(index)); + ValueUtils::moveAssignForward(valueIt + 1, moveValues().skip(index)); } - KeyUtils::moveAssign(keyIt, KeyMoveSlice{&key, 1}); + KeyUtils::moveAssign(keyIt, KeyMoveSpan{&key, 1}); m_count++; return valueIt; } auto addWithGrowing(Key key) -> Value* { - auto keyIt = const_cast(orderedKeys().lowerBound(key)); - if (keyIt != orderedKeys().end() && *keyIt == key) { + auto keyRange = orderedKeys().equalRange(key); + if (!keyRange.isEmpty()) { return nullptr; } + auto keyIt = const_cast(keyRange.begin()); auto newStorage = grownStorage(1); auto nKeyPtr = newStorage.begin(); - auto nValuePtr = toValuesPointer(newStorage.end()); - auto index = static_cast(keyIt - m_pointer); + auto nValuePtr = _toValuesPointer(newStorage.end()); + auto index = static_cast(keyIt - m_keyData); if (0 != index) { - KeyUtils::moveConstruct(nKeyPtr, KeyMoveSlice{m_pointer, index}); - ValueUtils::moveConstruct(nValuePtr, ValueMoveSlice{valuesPointer(), index}); + KeyUtils::moveConstruct(nKeyPtr, moveKeys().head(index)); + ValueUtils::moveConstruct(nValuePtr, moveValues().head(index)); nKeyPtr += index; nValuePtr += index; } - KeyUtils::moveConstruct(nKeyPtr, KeyMoveSlice{&key, 1}); + KeyUtils::moveConstruct(nKeyPtr, KeyMoveSpan{&key, 1}); // note: value is added outside if (index != m_count) { - KeyUtils::moveConstruct(nKeyPtr + 1, KeyMoveSlice{m_pointer + index, m_count - index}); - ValueUtils::moveConstruct(nValuePtr + 1, ValueMoveSlice{valuesPointer() + index, m_count - index}); + KeyUtils::moveConstruct(nKeyPtr + 1, moveKeys().skip(index)); + ValueUtils::moveConstruct(nValuePtr + 1, moveValues().skip(index)); } - deallocate(KeyAmendSlice{m_pointer, m_capacity}); - m_pointer = newStorage.begin(); + deallocate(_amendCapacity()); + m_keyData = newStorage.begin(); m_capacity = newStorage.count(); m_count++; return nValuePtr; } - auto inplaceMerge(KeyOrderedSlice newKeys, Value const* newValues) -> Index { + auto inplaceMerge(KeyConstOrderedSpan newKeys, Value const* newValues) -> Index { auto const less = Less{}; auto totalCount = newKeys.count() + m_count; - auto const keyPtr = m_pointer; - auto const valuePtr = valuesPointer(); - auto const oldBegin = m_pointer - 1; + auto const keyPtr = m_keyData; + auto const valuePtr = _valueData(); + auto const oldBegin = m_keyData - 1; auto oldIt = oldBegin + m_count; auto const newBegin = newKeys.begin() - 1; auto newIt = newBegin + newKeys.count(); @@ -333,13 +363,13 @@ template struct OrderedMapOf { auto constructCount = outIndex - m_count; outIndex = m_count; newCount -= constructCount; - KeyUtils::copyConstruct(keyPtr + outIndex, KeySlice{newBegin + 1 + newCount, constructCount}); - ValueUtils::copyConstruct(valuePtr + outIndex, ValueSlice{newValues + newCount, constructCount}); + KeyUtils::copyConstruct(keyPtr + outIndex, KeyConstSpan{newBegin + 1 + newCount, constructCount}); + ValueUtils::copyConstruct(valuePtr + outIndex, ValueConstSpan{newValues + newCount, constructCount}); } if (newCount > 0) { outIndex -= newCount; - KeyUtils::copyAssign(keyPtr + outIndex, KeySlice{newBegin + 1, newCount}); - ValueUtils::copyAssign(valuePtr + outIndex, ValueSlice{newValues, newCount}); + KeyUtils::copyAssign(keyPtr + outIndex, KeyConstSpan{newBegin + 1, newCount}); + ValueUtils::copyAssign(valuePtr + outIndex, ValueConstSpan{newValues, newCount}); } }; while (true) { @@ -355,17 +385,17 @@ template struct OrderedMapOf { outIndex = m_count; } count -= constructCount; - KeyUtils::moveConstruct(keyPtr + outIndex, KeyMoveSlice{oldB + 1 + count, constructCount}); + KeyUtils::moveConstruct(keyPtr + outIndex, KeyMoveSpan{oldB + 1 + count, constructCount}); ValueUtils::moveConstruct( valuePtr + outIndex, - ValueMoveSlice{valuePtr + (oldB - oldBegin + count), constructCount}); + ValueMoveSpan{valuePtr + (oldB - oldBegin + count), constructCount}); } if (count > 0) { outIndex -= count; - KeyUtils::moveAssignForward(keyPtr + outIndex, KeyMoveSlice{oldB + 1, count}); + KeyUtils::moveAssignForward(keyPtr + outIndex, KeyMoveSpan{oldB + 1, count}); ValueUtils::moveAssignForward( valuePtr + outIndex, - ValueMoveSlice{valuePtr + (oldB - oldBegin), count}); + ValueMoveSpan{valuePtr + (oldB - oldBegin), count}); } oldIt = oldB; if (oldIt == oldBegin) { @@ -393,15 +423,15 @@ template struct OrderedMapOf { outIndex = m_count; } count -= constructCount; - KeyUtils::copyConstruct(keyPtr + outIndex, KeySlice{newB + 1 + count, constructCount}); + KeyUtils::copyConstruct(keyPtr + outIndex, KeyConstSpan{newB + 1 + count, constructCount}); ValueUtils::copyConstruct( valuePtr + outIndex, - ValueSlice{newValues + (newB - newBegin) + count, constructCount}); + ValueConstSpan{newValues + (newB - newBegin) + count, constructCount}); } if (count > 0) { outIndex -= count; - KeyUtils::copyAssign(keyPtr + outIndex, KeySlice{newB + 1, count}); - ValueUtils::copyAssign(valuePtr + outIndex, ValueSlice{newValues + (newB - newBegin), count}); + KeyUtils::copyAssign(keyPtr + outIndex, KeyConstSpan{newB + 1, count}); + ValueUtils::copyAssign(valuePtr + outIndex, ValueConstSpan{newValues + (newB - newBegin), count}); } newIt = newB; if (newIt == newBegin) { @@ -414,20 +444,20 @@ template struct OrderedMapOf { auto offset = static_cast(oldIt - oldBegin); KeyUtils::moveAssignReverse( keyPtr + offset, - KeyMoveSlice{keyPtr + offset + equalCount, totalCount - offset}); + KeyMoveSpan{keyPtr + offset + equalCount, totalCount - offset}); ValueUtils::moveAssignReverse( valuePtr + offset, - ValueMoveSlice{valuePtr + offset + equalCount, totalCount - offset}); + ValueMoveSpan{valuePtr + offset + equalCount, totalCount - offset}); } return totalCount; } - auto mergeConstructInto(KeyAmendSlice newStorage, KeyOrderedSlice newKeys, Value const* newValues) -> Index { + auto mergeConstructInto(KeyAmendSpan newStorage, KeyConstOrderedSpan newKeys, Value const* newValues) -> Index { auto const less = Less{}; auto const keyPtr = newStorage.begin(); - auto const valuePtr = toValuesPointer(newStorage.end()); - auto const oldBegin = m_pointer; + auto const valuePtr = _toValuesPointer(newStorage.end()); + auto const oldBegin = m_keyData; auto const oldEnd = oldBegin + m_count; - auto const oldValuePtr = valuesPointer(); + auto const oldValuePtr = _valueData(); auto const newBegin = newKeys.begin(); auto const newEnd = newKeys.end(); auto oldIt = oldBegin; @@ -435,16 +465,14 @@ template struct OrderedMapOf { auto totalCount = Index{}; auto finishNew = [&]() { auto newCount = static_cast(newEnd - newIt); - KeyUtils::copyConstruct(keyPtr + totalCount, KeySlice{newIt, newCount}); - ValueUtils::copyConstruct(valuePtr + totalCount, ValueSlice{newValues + (newIt - newBegin), newCount}); + KeyUtils::copyConstruct(keyPtr + totalCount, KeyConstSpan{newIt, newCount}); + ValueUtils::copyConstruct(valuePtr + totalCount, ValueConstSpan{newValues + (newIt - newBegin), newCount}); totalCount += newCount; }; auto finishOld = [&]() { auto oldCount = static_cast(oldEnd - oldIt); - KeyUtils::moveConstruct(keyPtr + totalCount, KeyMoveSlice{oldIt, oldCount}); - ValueUtils::moveConstruct( - valuePtr + totalCount, - ValueMoveSlice{oldValuePtr + (oldIt - oldBegin), oldCount}); + KeyUtils::moveConstruct(keyPtr + totalCount, KeyMoveSpan{oldIt, oldCount}); + ValueUtils::moveConstruct(valuePtr + totalCount, ValueMoveSpan{oldValuePtr + (oldIt - oldBegin), oldCount}); totalCount += oldCount; }; @@ -462,8 +490,8 @@ template struct OrderedMapOf { newE += 1; } while (newE != newEnd && !less(*oldIt, *newE)); auto count = static_cast(newE - newIt); - KeyUtils::copyConstruct(keyPtr + totalCount, KeySlice{newIt, count}); - ValueUtils::copyConstruct(valuePtr + totalCount, ValueSlice{newValues + (newIt - newBegin), count}); + KeyUtils::copyConstruct(keyPtr + totalCount, KeyConstSpan{newIt, count}); + ValueUtils::copyConstruct(valuePtr + totalCount, ValueConstSpan{newValues + (newIt - newBegin), count}); totalCount += count; newIt = newE; if (newIt == newEnd) { @@ -475,10 +503,10 @@ template struct OrderedMapOf { auto oldE = oldIt + 1; while (oldE != oldEnd && less(*oldE, *newIt)) oldE += 1; auto count = static_cast(oldE - oldIt); - KeyUtils::moveConstruct(keyPtr + totalCount, KeyMoveSlice{oldIt, count}); + KeyUtils::moveConstruct(keyPtr + totalCount, KeyMoveSpan{oldIt, count}); ValueUtils::moveConstruct( valuePtr + totalCount, - ValueMoveSlice{oldValuePtr + (oldIt - oldBegin), count}); + ValueMoveSpan{oldValuePtr + (oldIt - oldBegin), count}); totalCount += count; oldIt = oldE; if (oldIt == oldEnd) { diff --git a/src/lookup19.lib/lookup19/OrderedMapOf.ostream.h b/src/lookup19.lib/lookup19/OrderedMapArray.ostream.h similarity index 85% rename from src/lookup19.lib/lookup19/OrderedMapOf.ostream.h rename to src/lookup19.lib/lookup19/OrderedMapArray.ostream.h index f6c27751..5488b751 100644 --- a/src/lookup19.lib/lookup19/OrderedMapOf.ostream.h +++ b/src/lookup19.lib/lookup19/OrderedMapArray.ostream.h @@ -1,5 +1,5 @@ #pragma once -#include "OrderedMapOf.h" +#include "OrderedMapArray.h" #include "array19/Zip.h" #include @@ -7,7 +7,7 @@ namespace lookup19 { template -auto operator<<(std::basic_ostream& out, OrderedMapOf const& s) -> decltype(out)& { +auto operator<<(std::basic_ostream& out, OrderedMapArray const& s) -> decltype(out)& { out << "{"; bool first = true; for (auto [key, value] : array19::Zip{s.keys(), s.values()}) { diff --git a/src/lookup19.lib/lookup19/OrderedMapArray.test.cpp b/src/lookup19.lib/lookup19/OrderedMapArray.test.cpp new file mode 100644 index 00000000..13d9e9e3 --- /dev/null +++ b/src/lookup19.lib/lookup19/OrderedMapArray.test.cpp @@ -0,0 +1,207 @@ +#include "OrderedMapArray.h" + +#include "OrderedMapArray.equals.h" +#include "OrderedMapArray.ostream.h" +#include "array19/Array.h" +#include "array19/Span.carray.h" +#include "array19/Span.equals.h" +#include "array19/Span.ostream.h" + +#include + +using namespace lookup19; + +TEST(OrderedMapArray, basics) { + auto v = OrderedMapArray{}; + + EXPECT_TRUE(v.isEmpty()); + EXPECT_FALSE(v.hasKey(17)); + EXPECT_EQ(v.totalCapacity(), 0u); + EXPECT_EQ(v.unusedCapacity(), 0u); + ASSERT_EQ(v.count(), 0u); + + v.ensureUnusedCapacity(1); + + EXPECT_GE(v.totalCapacity(), 1u); + EXPECT_GE(v.unusedCapacity(), 1u); + ASSERT_EQ(v.count(), 0u); + + v.insert(12, 1); + v.insert(23, 2); + v.insert(17, 3); + + ASSERT_EQ(v.count(), 3u); + EXPECT_TRUE(v.hasKey(17)); + EXPECT_FALSE(v.hasKey(42)); + EXPECT_GE(v.totalCapacity(), 3u); + EXPECT_EQ(v.keys(), static_cast>(array19::Array{12, 17, 23})); + EXPECT_EQ( + static_cast>(v.values()), + static_cast>(array19::Array{1, 3, 2})); + + v.insert(42, 4); + v.insert(12, 5); + + ASSERT_EQ(v.count(), 4u); + EXPECT_TRUE(v.hasKey(42)); + EXPECT_GE(v.totalCapacity(), 4u); + EXPECT_EQ( + static_cast>(v.orderedKeys()), + static_cast>(array19::Array{12, 17, 23, 42})); + EXPECT_EQ( + static_cast>(v.values()), + static_cast>(array19::Array{1, 3, 2, 4})); + + v.remove(OrderedSpan{v.orderedKeys()}.lowerBound(17)); + + ASSERT_EQ(v.count(), 3u); + EXPECT_FALSE(v.hasKey(17)); + EXPECT_EQ( + static_cast>(v.orderedKeys()), + static_cast>(array19::Array{12, 23, 42})); + EXPECT_EQ( + static_cast>(v.values()), + static_cast>(array19::Array{1, 2, 4})); + + auto v2 = v; + EXPECT_EQ(v, v2); + v.insert(13, 5); + EXPECT_NE(v, v2); + v2.emplace(13, 7); + EXPECT_NE(v, v2); + + *v.amendBy(13) = 7; + EXPECT_EQ(v, v2); +} + +using array19::Array; +using array19::spanOfCArray; + +TEST(OrderedMapArray, intoMergeBigIntoLittle) { + using Map = OrderedMapArray; + using KeySpan = OrderedSpan; + + // clang-format off + auto map = Map{ + KeySpan{Array{ 1, 3, 7}}, + Array{10, 11, 12}.begin(), + }; + map.mergeRange( // + KeySpan{Array{ 2, 3, 4, 5, 8}}, + Array{20, 21, 22, 23, 24}.begin()); + + auto expected = Map{ + KeySpan{Array{ 1, 2, 3, 4, 5, 7, 8}}, + Array{10, 20, 21, 22, 23, 12, 24}.begin(), + }; // ^^ duplicate + // clang-format on + EXPECT_EQ(map, expected); +} + +TEST(OrderedMapArray, intoMergeLittleIntoBig) { + using Map = OrderedMapArray; + using KeySpan = OrderedSpan; + + // clang-format off + auto map = Map{ + KeySpan{Array{ 2, 3, 4, 5, 8}}, + Array{20, 21, 22, 23, 24}.begin(), + }; + map.mergeRange( // + KeySpan{Array{ 1, 3, 7}}, + Array{10, 11, 12}.begin()); + + auto expected = Map{ + KeySpan{Array{ 1, 2, 3, 4, 5, 7, 8}}, + Array{10, 20, 11, 22, 23, 12, 24}.begin(), + }; // ^^ duplicate + // clang-format on + EXPECT_EQ(map, expected); +} + +TEST(OrderedMapArray, inplaceMergeBigIntoLittle) { + using Map = OrderedMapArray; + using KeySpan = OrderedSpan; + + auto map = Map{}; + // clang-format off + map.mergeRange( // + KeySpan{Array{ 1, 3, 7}}, + Array{10, 11, 12}.begin()); + map.ensureUnusedCapacity(5); + + map.mergeRange( // + KeySpan{Array{ 2, 3, 4, 5, 8}}, + Array{20, 21, 22, 23, 24}.begin()); + + auto expected = Map{ + KeySpan{Array{ 1, 2, 3, 4, 5, 7, 8}}, + Array{10, 20, 21, 22, 23, 12, 24}.begin(), + }; // ^^ duplicate + // clang-format on + EXPECT_EQ(map, expected); +} + +TEST(OrderedMapArray, inplaceMergeLittleIntoBig) { + using Map = OrderedMapArray; + using KeySpan = OrderedSpan; + + auto map = Map{}; + // clang-format off + map.mergeRange( // + KeySpan{Array{ 2, 3, 4, 5, 8}}, + Array{20, 21, 22, 23, 24}.begin()); + map.ensureUnusedCapacity(5); + + map.mergeRange( // + KeySpan{Array{ 1, 3, 7}}, + Array{10, 11, 12}.begin()); + + auto expected = Map{ + KeySpan{Array{ 1, 2, 3, 4, 5, 7, 8}}, + Array{10, 20, 11, 22, 23, 12, 24}.begin(), + }; // ^^ duplicate + // clang-format on + EXPECT_EQ(map, expected); +} + +TEST(OrderedMapArray, inplaceMergeAllFront) { + using Map = OrderedMapArray; + using KeySpan = OrderedSpan; + + auto map = Map{}; + // clang-format off + map.mergeRange( // + KeySpan{Array{ 8, 11, 13, 17}}, + Array{ 9, 10, 11, 12}.begin()); + map.ensureUnusedCapacity(5); + + map.mergeRange( // + KeySpan{Array{ 2, 3, 4, 5, 8}}, + Array{20, 21, 22, 23, 24}.begin()); + + auto expected = Map{ + KeySpan{Array{ 2, 3, 4, 5, 8, 11, 13, 17}}, + Array{20, 21, 22, 23, 24, 10, 11, 12}.begin(), + }; // ^^ duplicate + // clang-format on + EXPECT_EQ(map, expected); +} + +TEST(OrderedMapArray, uniquePtr) { + using Ptr = std::unique_ptr; + using Map = OrderedMapArray; + using KeyMoveSpan = OrderedSpan; + + auto a = Map{}; + a.emplace(5, new int{15}); + + auto b = std::move(a); + b.emplace(7, new int{17}); + + auto c = Map{}; + auto cValues = Array{Ptr{new int{15}}, Ptr{new int{17}}}; + c.mergeMoveRange(KeyMoveSpan{Array{5, 7}.move()}, cValues.move().begin()); + + EXPECT_NE(b, c); // note: compares pointers +} diff --git a/src/lookup19.lib/lookup19/OrderedMapOf.test.cpp b/src/lookup19.lib/lookup19/OrderedMapOf.test.cpp deleted file mode 100644 index b3efebd9..00000000 --- a/src/lookup19.lib/lookup19/OrderedMapOf.test.cpp +++ /dev/null @@ -1,202 +0,0 @@ -#include "OrderedMapOf.h" - -#include "OrderedMapOf.equals.h" -#include "OrderedMapOf.ostream.h" -#include "array19/Array.h" - -#include -#include -#include -#include - -using namespace lookup19; - -TEST(OrderedMapOf, basics) { - auto v = OrderedMapOf{}; - - EXPECT_TRUE(v.isEmpty()); - EXPECT_FALSE(v.hasKey(17)); - EXPECT_EQ(v.totalCapacity(), 0u); - EXPECT_EQ(v.unusedCapacity(), 0u); - ASSERT_EQ(v.count(), 0u); - - v.ensureUnusedCapacity(1); - - EXPECT_GE(v.totalCapacity(), 1u); - EXPECT_GE(v.unusedCapacity(), 1u); - ASSERT_EQ(v.count(), 0u); - - v.insert(12, 1); - v.insert(23, 2); - v.insert(17, 3); - - ASSERT_EQ(v.count(), 3u); - EXPECT_TRUE(v.hasKey(17)); - EXPECT_FALSE(v.hasKey(42)); - EXPECT_GE(v.totalCapacity(), 3u); - EXPECT_EQ(v.keys(), static_cast>(array19::Array{12, 17, 23})); - EXPECT_EQ( - static_cast>(v.values()), - static_cast>(array19::Array{1, 3, 2})); - - v.insert(42, 4); - v.insert(12, 5); - - ASSERT_EQ(v.count(), 4u); - EXPECT_TRUE(v.hasKey(42)); - EXPECT_GE(v.totalCapacity(), 4u); - EXPECT_EQ( - static_cast>(v.orderedKeys()), - static_cast>(array19::Array{12, 17, 23, 42})); - EXPECT_EQ( - static_cast>(v.values()), - static_cast>(array19::Array{1, 3, 2, 4})); - - v.remove(OrderedSliceOf{v.orderedKeys()}.lowerBound(17)); - - ASSERT_EQ(v.count(), 3u); - EXPECT_FALSE(v.hasKey(17)); - EXPECT_EQ( - static_cast>(v.orderedKeys()), - static_cast>(array19::Array{12, 23, 42})); - EXPECT_EQ( - static_cast>(v.values()), - static_cast>(array19::Array{1, 2, 4})); - - auto v2 = v; - EXPECT_EQ(v, v2); - v.insert(13, 5); - EXPECT_NE(v, v2); - v2.emplace(13, 7); - EXPECT_NE(v, v2); - - *v.amendBy(13) = 7; - EXPECT_EQ(v, v2); -} - -using array19::Array; - -TEST(OrderedMapOf, intoMerge) { - using IM = OrderedMapOf; - - // clang-format off - auto aKeys = Array{ 1, 3, 7}; - auto aValues = Array{10, 11, 12}; - // clang-format on - auto a = IM{OrderedSliceOf{aKeys}, aValues.begin()}; - - // clang-format off - auto bKeys = Array{ 2, 3, 4, 5, 8}; - auto bValues = Array{20, 21, 22, 23, 24}; - // clang-format on - // auto b = IM(OrderedSliceOf{bKeys}, bValues.begin()); - - a.mergeRange(OrderedSliceOf{bKeys}, bValues.begin()); - - // clang-format off - auto mKeys = Array{ 1, 2, 3, 4, 5, 7, 8}; - auto mValues = Array{10, 20, 21, 22, 23, 12, 24}; - // clang-format on - auto m = IM{OrderedSliceOf{mKeys}, mValues.begin()}; - EXPECT_EQ(a, m); -} - -TEST(OrderedMapOf, intoMerge2) { - using IM = OrderedMapOf; - - // clang-format off - auto aKeys = Array{ 2, 3, 4, 5, 8}; - auto aValues = Array{20, 21, 22, 23, 24}; - // clang-format on - auto a = IM{OrderedSliceOf{aKeys}, aValues.begin()}; - - // clang-format off - auto bKeys = Array{ 1, 3, 7}; - auto bValues = Array{10, 11, 12}; - // clang-format on - // auto b = IM(OrderedSliceOf{bKeys}, bValues.begin()); - - a.mergeRange(OrderedSliceOf{bKeys}, bValues.begin()); - - // clang-format off - auto mKeys = Array{ 1, 2, 3, 4, 5, 7, 8}; - auto mValues = Array{10, 20, 11, 22, 23, 12, 24}; - // clang-format on - auto m = IM{OrderedSliceOf{mKeys}, mValues.begin()}; - EXPECT_EQ(a, m); -} - -TEST(OrderedMapOf, inplaceMerge) { - using IM = OrderedMapOf; - - auto a = IM{}; - // clang-format off - auto aKeys = Array{ 1, 3, 7}; - auto aValues = Array{10, 11, 12}; - // clang-format on - a.mergeRange(OrderedSliceOf{aKeys}, aValues.begin()); - a.ensureUnusedCapacity(5); - - // clang-format off - auto bKeys = Array{ 2, 3, 4, 5, 8}; - auto bValues = Array{20, 21, 22, 23, 24}; - // clang-format on - a.mergeRange(OrderedSliceOf{bKeys}, bValues.begin()); - - // clang-format off - auto mKeys = Array{ 1, 2, 3, 4, 5, 7, 8}; - auto mValues = Array{10, 20, 21, 22, 23, 12, 24}; - // clang-format on - auto m = IM{OrderedSliceOf{mKeys}, mValues.begin()}; - EXPECT_EQ(a, m); -} - -TEST(OrderedMapOf, inplaceMerge2) { - using IM = OrderedMapOf; - - auto a = IM{}; - // clang-format off - auto aKeys = Array{ 2, 3, 4, 5, 8}; - auto aValues = Array{20, 21, 22, 23, 24}; - // clang-format on - a.mergeRange(OrderedSliceOf{aKeys}, aValues.begin()); - a.ensureUnusedCapacity(5); - - // clang-format off - auto bKeys = Array{ 1, 3, 7}; - auto bValues = Array{10, 11, 12}; - // clang-format on - a.mergeRange(OrderedSliceOf{bKeys}, bValues.begin()); - - // clang-format off - auto mKeys = Array{ 1, 2, 3, 4, 5, 7, 8}; - auto mValues = Array{10, 20, 11, 22, 23, 12, 24}; - // clang-format on - auto m = IM{OrderedSliceOf{mKeys}, mValues.begin()}; - EXPECT_EQ(a, m); -} - -TEST(OrderedMapOf, inplaceMergeAllFront) { - using IM = OrderedMapOf; - - auto a = IM{}; - // clang-format off - auto aKeys = Array{ 8, 11, 13, 17}; - auto aValues = Array{ 9, 10, 11, 12}; - // clang-format on - a.mergeRange(OrderedSliceOf{aKeys}, aValues.begin()); - a.ensureUnusedCapacity(5); - - // clang-format off - auto bKeys = Array{ 2, 3, 4, 5, 8}; - auto bValues = Array{20, 21, 22, 23, 24}; - // clang-format on - a.mergeRange(OrderedSliceOf{bKeys}, bValues.begin()); - - // clang-format off - auto mKeys = Array{ 2, 3, 4, 5, 8, 11, 13, 17}; - auto mValues = Array{20, 21, 22, 23, 24, 10, 11, 12}; - // clang-format on - auto m = IM{OrderedSliceOf{mKeys}, mValues.begin()}; - EXPECT_EQ(a, m); -} diff --git a/src/lookup19.lib/lookup19/OrderedSliceOf.h b/src/lookup19.lib/lookup19/OrderedSliceOf.h deleted file mode 100644 index 5a45c337..00000000 --- a/src/lookup19.lib/lookup19/OrderedSliceOf.h +++ /dev/null @@ -1,100 +0,0 @@ -#pragma once -#include "array19/SliceOf.h" - -#include // size_t - -namespace lookup19 { - -using array19::SliceOf; - -/// Readonly SliceOf an ordered array -/// -/// note: -/// * if we allow modifications ordering might be violated -template struct OrderedSliceOf { - using Element = const T; - using Count = size_t; - using Index = size_t; - -private: - Element* m_data{}; - Count m_count{}; - -public: - constexpr OrderedSliceOf() = default; - constexpr explicit OrderedSliceOf(Element* data, Element* end) noexcept - : m_data{data} - , m_count{static_cast(end - data)} {} - constexpr explicit OrderedSliceOf(Element* data, Count count) noexcept : m_data{data}, m_count{count} {} - constexpr explicit OrderedSliceOf(SliceOf slice) noexcept : m_data{slice.begin()}, m_count{slice.count()} {} - - [[nodiscard]] constexpr auto isEmpty() const noexcept -> bool { return m_count == 0; } - [[nodiscard]] constexpr auto count() const -> Count { return m_count; } - [[nodiscard]] constexpr auto begin() const& noexcept -> Element* { return m_data; } - [[nodiscard]] constexpr auto end() const& -> Element* { return m_data + m_count; } - [[nodiscard]] constexpr auto operator[](Index index) const noexcept -> Element& { return *(m_data + index); } - - [[nodiscard]] constexpr operator SliceOf() const noexcept { return SliceOf{m_data, m_count}; } - - template [[nodiscard]] constexpr auto lowerBound(K&& key) -> Element* { - auto less = Less{}; - auto lower_bound = m_data; - auto count = m_count; - while (count > 0) { - auto half = count >> 1; - if (less(lower_bound[half], key)) { - lower_bound += half + 1; - count -= half + 1; - } - else { - count = half; - } - } - return lower_bound; - } - - template [[nodiscard]] constexpr auto upperBound(K&& key) -> Element* { - auto less = Less{}; - auto upper_bound = m_data; - auto count = m_count; - while (count > 0) { - auto half = count >> 1; - if (!less(key, upper_bound[half])) { - upper_bound += half + 1; - count -= half + 1; - } - else { - count = half; - } - } - return upper_bound; - } - - template [[nodiscard]] constexpr auto equalRange(K&& key) -> OrderedSliceOf { - auto less = Less{}; - auto count = m_count; - auto lower_bound = m_data; - auto upper_bound = m_data + count; - while (count > 0) { - auto half = count >> 1; - if (less(lower_bound[half], key)) { - lower_bound += half + 1; - count -= half + 1; - } - else if (less(key, lower_bound[half])) { - upper_bound = lower_bound + half; - count = half; - } - else { - auto l = OrderedSliceOf{lower_bound, lower_bound + half}.lowerBound(key); - auto u = OrderedSliceOf{lower_bound + half + 1, upper_bound}.upperBound(key); - return OrderedSliceOf{l, u}; - } - } - return OrderedSliceOf{lower_bound, upper_bound}; - } -}; - -template struct OrderedSliceOf; // use MoveSliceOf for that - -} // namespace lookup19 diff --git a/src/lookup19.lib/lookup19/OrderedSpan.h b/src/lookup19.lib/lookup19/OrderedSpan.h new file mode 100644 index 00000000..8ecbd883 --- /dev/null +++ b/src/lookup19.lib/lookup19/OrderedSpan.h @@ -0,0 +1,114 @@ +#pragma once +#include "array19/Span.h" + +#include // size_t + +namespace lookup19 { + +using array19::Span; + +/// Readonly Span an ordered array +/// +/// note: +/// * if we allow modifications ordering might be violated +template struct OrderedSpan : Span { + using Data = typename Span::Data; + using Less = L; + using Count = size_t; + using Index = size_t; + +public: + constexpr OrderedSpan() = default; + constexpr explicit OrderedSpan(Data* data, Count count) noexcept : Span{data, count} {} + explicit constexpr OrderedSpan(Span span) noexcept : Span{span} {} + + // requires: offset + count <= count() + [[nodiscard]] constexpr auto slice(Index offset, Count count) const noexcept -> OrderedSpan { + return OrderedSpan{this->begin() + offset, count}; + } + // requires: offset <= count() + [[nodiscard]] constexpr auto skip(Index offset) const noexcept -> OrderedSpan { + return OrderedSpan{this->begin() + offset, static_cast(this->count() - offset)}; + } + // requires: count <= count() + [[nodiscard]] constexpr auto head(Count count) const noexcept -> OrderedSpan { + return OrderedSpan{this->begin(), count}; // + } + // requires: count <= count() + [[nodiscard]] constexpr auto tail(Count count) const noexcept -> OrderedSpan { + return OrderedSpan{this->begin() + this->count() - count, count}; + } + // requires: count <= count() + [[nodiscard]] constexpr auto drop_tail(Count count) const noexcept -> OrderedSpan { + return OrderedSpan{this->begin(), static_cast(this->count() - count)}; + } + + template [[nodiscard]] constexpr auto lowerBound(Key&& key) -> Data* { + auto less = Less{}; + auto lower_bound = this->begin(); + auto count = this->count(); + while (count > 0) { + auto half = count >> 1; + if (less(lower_bound[half], key)) { + lower_bound += half + 1; + count -= half + 1; + } + else { + count = half; + } + } + return lower_bound; + } + + template [[nodiscard]] constexpr auto upperBound(Key&& key) -> Data* { + auto less = Less{}; + auto upper_bound = this->begin(); + auto count = this->count(); + while (count > 0) { + auto half = count >> 1; + if (!less(key, upper_bound[half])) { + upper_bound += half + 1; + count -= half + 1; + } + else { + count = half; + } + } + return upper_bound; + } + + template [[nodiscard]] constexpr auto equalRange(Key&& key) -> OrderedSpan { + auto less = Less{}; + auto result = OrderedSpan{*this}; + while (result.count() > 0) { + auto half = result.count() >> 1; + if (less(result[half], key)) { + result = result.skip(half + 1); + } + else if (less(key, result[half])) { + result = result.head(half); + } + else { + auto l = result.head(half).lowerBound(key); + auto u = result.skip(half + 1).upperBound(key); + return OrderedSpan{l, static_cast(u - l)}; + } + } + return result; + } +}; + +template struct OrderedSpan; // wont work - use OrderedSpan +template struct OrderedSpan; // that's nonsense + +template constexpr auto orderedSpan(T const* data, size_t count) -> OrderedSpan { + return OrderedSpan{data, count}; +} +template constexpr auto amendOrderedSpan(T* data, size_t count) -> OrderedSpan { + return OrderedSpan{data, count}; +} +template constexpr auto moveOrderedSpan(T* data, size_t count) -> OrderedSpan { + return OrderedSpan{data, count}; +} + +} // namespace lookup19 diff --git a/src/lookup19.lib/lookup19/lookup19.qbs b/src/lookup19.lib/lookup19/lookup19.qbs index 01bc2452..f581c77d 100644 --- a/src/lookup19.lib/lookup19/lookup19.qbs +++ b/src/lookup19.lib/lookup19/lookup19.qbs @@ -12,9 +12,9 @@ Product { } files: [ - "OrderedMapOf.equals.h", - "OrderedMapOf.h", - "OrderedMapOf.ostream.h", - "OrderedSliceOf.h", + "OrderedMapArray.equals.h", + "OrderedMapArray.h", + "OrderedMapArray.ostream.h", + "OrderedSpan.h", ] } diff --git a/src/lookup19.lib/lookup19/lookup19.tests.qbs b/src/lookup19.lib/lookup19/lookup19.tests.qbs index 596229f4..f980df46 100644 --- a/src/lookup19.lib/lookup19/lookup19.tests.qbs +++ b/src/lookup19.lib/lookup19/lookup19.tests.qbs @@ -13,6 +13,6 @@ Application { // cpp.combineCxxSources: true files: [ - "OrderedMapOf.test.cpp", + "OrderedMapArray.test.cpp", ] } diff --git a/src/partial19.lib/partial19/Partial.h b/src/partial19.lib/partial19/Partial.h index ba63d813..e81f8b90 100644 --- a/src/partial19.lib/partial19/Partial.h +++ b/src/partial19.lib/partial19/Partial.h @@ -1,8 +1,8 @@ #pragma once #include "Bitset.h" #include "align.h" -#include "array19/SliceOf.carray.h" -#include "array19/SliceOf.max.h" +#include "array19/Span.carray.h" +#include "array19/Span.max.h" #include "meta19/Index.h" #include "meta19/RemoveReference.h" #include "meta19/TypeAt.h" @@ -15,8 +15,8 @@ namespace partial19 { -using array19::sliceMaximum; -using array19::sliceOfCArray; +using array19::spanMaximum; +using array19::spanOfCArray; using meta19::_index; using meta19::Index; using meta19::index_of_map; @@ -87,7 +87,7 @@ template struct Partial { using Map = typename Which::Map; static constexpr size_t sizeof_ts[] = {sizeof(Ts)...}; static constexpr size_t alignof_ts[] = {alignof(Ts)...}; - enum : size_t { count = sizeof...(Ts), max_align = sliceMaximum(sliceOfCArray(alignof_ts)) }; + enum : size_t { count = sizeof...(Ts), max_align = spanMaximum(spanOfCArray(alignof_ts)) }; private: Bits m_bits{}; diff --git a/src/serialize19.lib/serialize19/BufferSlice.h b/src/serialize19.lib/serialize19/BufferSlice.h deleted file mode 100644 index 006f12e8..00000000 --- a/src/serialize19.lib/serialize19/BufferSlice.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once -#include "array19/SliceOf.h" - -#include // uint8_t - -namespace serialize19 { - -using array19::SliceOf; -using BufferSlice = SliceOf; - -} // namespace serialize19 diff --git a/src/serialize19.lib/serialize19/BufferSpan.h b/src/serialize19.lib/serialize19/BufferSpan.h new file mode 100644 index 00000000..67918ec5 --- /dev/null +++ b/src/serialize19.lib/serialize19/BufferSpan.h @@ -0,0 +1,11 @@ +#pragma once +#include "array19/Span.h" + +#include // uint8_t + +namespace serialize19 { + +using array19::Span; +using BufferSpan = Span; + +} // namespace serialize19 diff --git a/src/serialize19.lib/serialize19/Codec.test.cpp b/src/serialize19.lib/serialize19/Codec.test.cpp index 06636ad0..f7c233e9 100644 --- a/src/serialize19.lib/serialize19/Codec.test.cpp +++ b/src/serialize19.lib/serialize19/Codec.test.cpp @@ -38,7 +38,7 @@ TEST(Codec, Person) { auto input = Person{"Santa Clause", -100, Profession::Guru}; auto buffer = dynamicWrite(input); - auto reader = ReadArchive{buffer.slice()}; + auto reader = ReadArchive{buffer.span()}; auto output = Person{}; serialize(reader, output); diff --git a/src/serialize19.lib/serialize19/Endianess.h b/src/serialize19.lib/serialize19/Endianess.h index fd863d3d..ea02c04d 100644 --- a/src/serialize19.lib/serialize19/Endianess.h +++ b/src/serialize19.lib/serialize19/Endianess.h @@ -7,7 +7,7 @@ namespace serialize19 { using array19::Array; -/// use by SliceReader & WriteAppender to mark change in endianess +/// use by SpanReader & WriteAppender to mark change in endianess /// note: compare host to target endianness enum class EndianBehaviour : bool { Keep, Flip }; diff --git a/src/serialize19.lib/serialize19/ReadArchive.h b/src/serialize19.lib/serialize19/ReadArchive.h index a21ea8f9..9d5ab50c 100644 --- a/src/serialize19.lib/serialize19/ReadArchive.h +++ b/src/serialize19.lib/serialize19/ReadArchive.h @@ -1,6 +1,6 @@ #pragma once #include "Archive.h" -#include "SliceReader.h" +#include "SpanReader.h" #include "meta19/Type.h" #include "serialize.h" @@ -11,32 +11,32 @@ namespace serialize19 { using meta19::type; /// Archive Adapter that allows reading from serialize() methods -/// see SliceReader +/// see SpanReader template struct ReadArchive { static constexpr auto mode = ArchiveMode::Read; - ReadArchive(BufferSlice slice) : m_slice(slice) {} + ReadArchive(BufferSpan span) : m_span(span) {} - auto slice() const -> BufferSlice { return m_slice; } + auto span() const -> BufferSpan { return m_span; } - auto withSlice(size_t n) -> BufferSlice { - auto r = m_slice.sliceBytes(n); - m_slice = m_slice.skipBytes(n); + auto withSpan(size_t n) -> BufferSpan { + auto r = m_span.sliceBytes(n); + m_span = m_span.skipBytes(n); return r; } template void withPrimitive(T& value) { - value = m_slice.as(type); - m_slice = m_slice.skipOf(type); + value = m_span.as(type); + m_span = m_span.skipOf(type); } void withPrimitive(bool& value) { - value = m_slice.as(type) == 1; - m_slice = m_slice.skipOf(type); + value = m_span.as(type) == 1; + m_span = m_span.skipOf(type); } private: - SliceReader m_slice; + SpanReader m_span; }; static_assert(Archive>); diff --git a/src/serialize19.lib/serialize19/SizeAppender.h b/src/serialize19.lib/serialize19/SizeAppender.h index bd39b57e..90ec292e 100644 --- a/src/serialize19.lib/serialize19/SizeAppender.h +++ b/src/serialize19.lib/serialize19/SizeAppender.h @@ -1,11 +1,11 @@ #pragma once -#include "array19/SliceOf.h" +#include "array19/Span.h" #include // size_t namespace serialize19 { -using array19::SliceOf; +using array19::Span; /// accumulates the size of everything that is appended to this struct SizeAppender { @@ -15,8 +15,8 @@ struct SizeAppender { constexpr auto count() const -> size_t { return m_count; } template constexpr auto appendValue(T) const -> SizeAppender { return SizeAppender{m_count + sizeof(T)}; } - template constexpr auto appendSlice(SliceOf slice) const -> SizeAppender { - return SizeAppender{m_count + sizeof(T) * slice.count()}; + template constexpr auto appendSpan(Span span) const -> SizeAppender { + return SizeAppender{m_count + sizeof(T) * span.count()}; } private: diff --git a/src/serialize19.lib/serialize19/SizeArchive.h b/src/serialize19.lib/serialize19/SizeArchive.h index 274bb376..9657fd40 100644 --- a/src/serialize19.lib/serialize19/SizeArchive.h +++ b/src/serialize19.lib/serialize19/SizeArchive.h @@ -1,6 +1,6 @@ #pragma once #include "Archive.h" -#include "BufferSlice.h" +#include "BufferSpan.h" #include "SizeAppender.h" #include "serialize.h" @@ -16,7 +16,7 @@ struct SizeArchive { constexpr auto size() const -> size_t { return m_appender.count(); } - void withSlice(BufferSlice slice) { m_appender = m_appender.appendSlice(slice); } + void withSpan(BufferSpan span) { m_appender = m_appender.appendSpan(span); } template void withPrimitive(const T& value) { m_appender = m_appender.appendValue(value); } diff --git a/src/serialize19.lib/serialize19/SliceReader.h b/src/serialize19.lib/serialize19/SliceReader.h deleted file mode 100644 index 5192e620..00000000 --- a/src/serialize19.lib/serialize19/SliceReader.h +++ /dev/null @@ -1,70 +0,0 @@ -#pragma once -#include "BufferSlice.h" -#include "Endianess.h" -#include "meta19/Type.h" - -#include // size_t - -namespace serialize19 { - -using meta19::Type; - -namespace details { - -struct SliceReaderCommon { - constexpr SliceReaderCommon() = default; - constexpr explicit SliceReaderCommon(const uint8_t* data, size_t count) : m_data(data), m_count(count) {} - constexpr SliceReaderCommon(BufferSlice slice) : SliceReaderCommon(slice.begin(), slice.count()) {} - - constexpr auto count() const -> size_t { return m_count; } - constexpr operator BufferSlice() const { return BufferSlice{m_data, m_count}; } - - auto sliceBytes(size_t count) const -> BufferSlice { return SliceOf{m_data, count}; } - constexpr auto limitBytes(size_t count) const -> SliceReaderCommon { return SliceReaderCommon{m_data, count}; } - - constexpr auto skipBytes(size_t count) const -> SliceReaderCommon { - return SliceReaderCommon{m_data + count, m_count - count}; - } - template constexpr auto skipOf(Type* = {}) const -> SliceReaderCommon { - return SliceReaderCommon{m_data + sizeof(T), m_count - sizeof(T)}; - } - template constexpr auto skipCountOf(size_t count, Type* = {}) const -> SliceReaderCommon { - return SliceReaderCommon{m_data + count * sizeof(T), m_count - count * sizeof(T)}; - } - -protected: - const uint8_t* m_data{}; - size_t m_count{}; -}; - -} // namespace details - -template struct SliceReader; - -template<> struct SliceReader final : details::SliceReaderCommon { - using details::SliceReaderCommon::SliceReaderCommon; - using details::SliceReaderCommon::operator=; - - template constexpr auto as(Type* = {}) const -> T { return *(const T*)m_data; } - template auto sliceCountOf(size_t count, Type* = {}) const -> SliceOf { - return {(const T*)m_data, count}; - } - template void eachCountOf(size_t count, Type*, F&& f) const { - auto p = (const T*)m_data; - for (auto i = 0u; i < count; ++i) f(*p++); - } -}; - -template<> struct SliceReader final : details::SliceReaderCommon { - using details::SliceReaderCommon::SliceReaderCommon; - using details::SliceReaderCommon::operator=; - - template constexpr auto as(Type* = {}) const -> T { return endianFlipFor(*(const T*)m_data); } - - template void eachCountOf(size_t count, Type*, F&& f) const { - auto p = (const T*)m_data; - for (auto i = 0u; i < count; ++i) f(endianFlipFor(*p++)); - } -}; - -} // namespace serialize19 diff --git a/src/serialize19.lib/serialize19/SpanReader.h b/src/serialize19.lib/serialize19/SpanReader.h new file mode 100644 index 00000000..b13edb20 --- /dev/null +++ b/src/serialize19.lib/serialize19/SpanReader.h @@ -0,0 +1,70 @@ +#pragma once +#include "BufferSpan.h" +#include "Endianess.h" +#include "meta19/Type.h" + +#include // size_t + +namespace serialize19 { + +using meta19::Type; + +namespace details { + +struct SpanReaderCommon { + constexpr SpanReaderCommon() = default; + constexpr explicit SpanReaderCommon(const uint8_t* data, size_t count) : m_data(data), m_count(count) {} + constexpr SpanReaderCommon(BufferSpan span) : SpanReaderCommon(span.begin(), span.count()) {} + + constexpr auto count() const -> size_t { return m_count; } + constexpr operator BufferSpan() const { return BufferSpan{m_data, m_count}; } + + auto sliceBytes(size_t count) const -> BufferSpan { return Span{m_data, count}; } + constexpr auto limitBytes(size_t count) const -> SpanReaderCommon { return SpanReaderCommon{m_data, count}; } + + constexpr auto skipBytes(size_t count) const -> SpanReaderCommon { + return SpanReaderCommon{m_data + count, m_count - count}; + } + template constexpr auto skipOf(Type* = {}) const -> SpanReaderCommon { + return SpanReaderCommon{m_data + sizeof(T), m_count - sizeof(T)}; + } + template constexpr auto skipCountOf(size_t count, Type* = {}) const -> SpanReaderCommon { + return SpanReaderCommon{m_data + count * sizeof(T), m_count - count * sizeof(T)}; + } + +protected: + const uint8_t* m_data{}; + size_t m_count{}; +}; + +} // namespace details + +template struct SpanReader; + +template<> struct SpanReader final : details::SpanReaderCommon { + using details::SpanReaderCommon::SpanReaderCommon; + using details::SpanReaderCommon::operator=; + + template constexpr auto as(Type* = {}) const -> T { return *(const T*)m_data; } + template auto sliceCountOf(size_t count, Type* = {}) const -> Span { + return {(const T*)m_data, count}; + } + template void eachCountOf(size_t count, Type*, F&& f) const { + auto p = (const T*)m_data; + for (auto i = 0u; i < count; ++i) f(*p++); + } +}; + +template<> struct SpanReader final : details::SpanReaderCommon { + using details::SpanReaderCommon::SpanReaderCommon; + using details::SpanReaderCommon::operator=; + + template constexpr auto as(Type* = {}) const -> T { return endianFlipFor(*(const T*)m_data); } + + template void eachCountOf(size_t count, Type*, F&& f) const { + auto p = (const T*)m_data; + for (auto i = 0u; i < count; ++i) f(endianFlipFor(*p++)); + } +}; + +} // namespace serialize19 diff --git a/src/serialize19.lib/serialize19/UniqueBuffer.h b/src/serialize19.lib/serialize19/UniqueBuffer.h index 79f12f68..058b5592 100644 --- a/src/serialize19.lib/serialize19/UniqueBuffer.h +++ b/src/serialize19.lib/serialize19/UniqueBuffer.h @@ -1,15 +1,15 @@ #pragma once -#include "BufferSlice.h" +#include "BufferSpan.h" #include // size_t namespace serialize19 { /// UniqueBuffer is allocated on construction and carries the ownership of the memory -/// Use slices to access the data +/// Use spans to access the data struct UniqueBuffer { - using Slice = SliceOf; - using ConstSlice = BufferSlice; + using ConstSpan = BufferSpan; + using AmendSpan = Span; UniqueBuffer() = default; explicit UniqueBuffer(size_t size) : m_pointer(new uint8_t[size]), m_size(size) {} @@ -25,8 +25,8 @@ struct UniqueBuffer { } [[nodiscard]] auto size() const -> size_t { return m_size; } - [[nodiscard]] auto slice() const -> ConstSlice { return ConstSlice{m_pointer, m_size}; } - [[nodiscard]] auto amendSlice() -> Slice { return Slice{m_pointer, m_size}; } + [[nodiscard]] auto span() const -> ConstSpan { return ConstSpan{m_pointer, m_size}; } + [[nodiscard]] auto amend() -> AmendSpan { return AmendSpan{m_pointer, m_size}; } private: uint8_t* m_pointer{}; diff --git a/src/serialize19.lib/serialize19/WriteAppender.h b/src/serialize19.lib/serialize19/WriteAppender.h index 751288f8..6a10789a 100644 --- a/src/serialize19.lib/serialize19/WriteAppender.h +++ b/src/serialize19.lib/serialize19/WriteAppender.h @@ -1,12 +1,12 @@ #pragma once #include "Endianess.h" -#include "array19/SliceOf.h" +#include "array19/Span.h" #include // uint8_t namespace serialize19 { -using array19::SliceOf; +using array19::Span; namespace details { @@ -30,9 +30,9 @@ template<> struct WriteAppender final : details::WriteApp *(T*)m_data = value; return WriteAppender{m_data + sizeof(T)}; } - template auto appendSlice(SliceOf slice) const -> WriteAppender { + template auto appendSpan(Span span) const -> WriteAppender { auto p = (T*)m_data; - for (auto v : slice) *p++ = v; + for (auto v : span) *p++ = v; return WriteAppender{(uint8_t*)p}; } }; @@ -44,9 +44,9 @@ template<> struct WriteAppender final : details::WriteApp *(T*)m_data = endianFlipFor(value); return WriteAppender{m_data + sizeof(T)}; } - template auto appendSlice(SliceOf slice) const -> WriteAppender { + template auto appendSpan(Span span) const -> WriteAppender { auto p = (T*)m_data; - for (auto v : slice) *p++ = endianFlipFor(v); + for (auto v : span) *p++ = endianFlipFor(v); return WriteAppender{(uint8_t*)p}; } }; diff --git a/src/serialize19.lib/serialize19/WriteToArchive.h b/src/serialize19.lib/serialize19/WriteToArchive.h index 17e3e890..f80377ad 100644 --- a/src/serialize19.lib/serialize19/WriteToArchive.h +++ b/src/serialize19.lib/serialize19/WriteToArchive.h @@ -1,19 +1,19 @@ #pragma once #include "Archive.h" -#include "BufferSlice.h" +#include "BufferSpan.h" #include "WriteAppender.h" #include "serialize.h" namespace serialize19 { -/// Archive adapter for writing to a slice of memory +/// Archive adapter for writing to a span of memory /// see: WriteAppender template struct WriteToArchive { static constexpr auto mode = ArchiveMode::Write; - WriteToArchive(SliceOf slice) : m_appender(slice.begin()) {} + WriteToArchive(Span span) : m_appender(span.begin()) {} - void withSlice(BufferSlice slice) { m_appender = m_appender.appendSlice(slice); } + void withSpan(BufferSpan span) { m_appender = m_appender.appendSpan(span); } template void withPrimitive(const T& value) { m_appender = m_appender.appendValue(value); } diff --git a/src/serialize19.lib/serialize19/dynamicWrite.h b/src/serialize19.lib/serialize19/dynamicWrite.h index ddd53779..0fc7e31a 100644 --- a/src/serialize19.lib/serialize19/dynamicWrite.h +++ b/src/serialize19.lib/serialize19/dynamicWrite.h @@ -10,7 +10,7 @@ template auto dynamicWr auto sizeArchive = SizeArchive{}; serialize(sizeArchive, value); auto buffer = UniqueBuffer{sizeArchive.size()}; - auto writeArchive = WriteToArchive{buffer.amendSlice()}; + auto writeArchive = WriteToArchive{buffer.amend()}; serialize(writeArchive, value); return buffer; } diff --git a/src/serialize19.lib/serialize19/serialize.AllocatedArrayOf.h b/src/serialize19.lib/serialize19/serialize.AllocatedArray.h similarity index 73% rename from src/serialize19.lib/serialize19/serialize.AllocatedArrayOf.h rename to src/serialize19.lib/serialize19/serialize.AllocatedArray.h index 2ede7d8e..073bfb62 100644 --- a/src/serialize19.lib/serialize19/serialize.AllocatedArrayOf.h +++ b/src/serialize19.lib/serialize19/serialize.AllocatedArray.h @@ -1,25 +1,25 @@ -#pragma once -#include "array19/AllocatedArrayOf.h" -#include "serialize.h" - -#include // uint32_t - -namespace serialize19 { - -using array19::AllocatedArrayOf; - -template void serialize(A& a, AllocatedArrayOf& allocatedArray) { - auto count = static_cast(allocatedArray.count()); - serialize(a, count); - if constexpr (A::mode == ArchiveMode::Read) { - allocatedArray = AllocatedArrayOf::createCount(count); - for (auto i = 0U; i < count; i++) { - serialize(a, allocatedArray.amend()[i]); - } - } - else { - for (auto& value : allocatedArray) serialize(a, value); - } -} - -} // namespace serialize19 +#pragma once +#include "array19/AllocatedArray.h" +#include "serialize.h" + +#include // uint32_t + +namespace serialize19 { + +using array19::AllocatedArray; + +template void serialize(A& a, AllocatedArray& allocatedArray) { + auto count = static_cast(allocatedArray.count()); + serialize(a, count); + if constexpr (A::mode == ArchiveMode::Read) { + allocatedArray = AllocatedArray::createCount(count); + for (auto i = 0U; i < count; i++) { + serialize(a, allocatedArray.amend()[i]); + } + } + else { + for (auto& value : allocatedArray) serialize(a, value); + } +} + +} // namespace serialize19 diff --git a/src/serialize19.lib/serialize19/serialize.BufferSlice.h b/src/serialize19.lib/serialize19/serialize.BufferSlice.h deleted file mode 100644 index 25037b6f..00000000 --- a/src/serialize19.lib/serialize19/serialize.BufferSlice.h +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once -#include "BufferSlice.h" -#include "serialize.h" - -namespace serialize19 { - -template void serialize(A& a, BufferSlice& slice) { - auto count = static_cast(slice.count()); - serialize(a, count); - if constexpr (A::mode == ArchiveMode::Read) { - slice = a.withSlice(count); - } - else { - a.withSlice(slice); - } -} - -} // namespace serialize19 diff --git a/src/serialize19.lib/serialize19/serialize.BufferSpan.h b/src/serialize19.lib/serialize19/serialize.BufferSpan.h new file mode 100644 index 00000000..63765dab --- /dev/null +++ b/src/serialize19.lib/serialize19/serialize.BufferSpan.h @@ -0,0 +1,18 @@ +#pragma once +#include "BufferSpan.h" +#include "serialize.h" + +namespace serialize19 { + +template void serialize(A& a, BufferSpan& span) { + auto count = static_cast(span.count()); + serialize(a, count); + if constexpr (A::mode == ArchiveMode::Read) { + span = a.withSpan(count); + } + else { + a.withSpan(span); + } +} + +} // namespace serialize19 diff --git a/src/serialize19.lib/serialize19/serialize.BufferSlice.test.cpp b/src/serialize19.lib/serialize19/serialize.BufferSpan.test.cpp similarity index 61% rename from src/serialize19.lib/serialize19/serialize.BufferSlice.test.cpp rename to src/serialize19.lib/serialize19/serialize.BufferSpan.test.cpp index 080379db..762b46bc 100644 --- a/src/serialize19.lib/serialize19/serialize.BufferSlice.test.cpp +++ b/src/serialize19.lib/serialize19/serialize.BufferSpan.test.cpp @@ -1,24 +1,24 @@ -#include -#include -#include -#include -#include -#include - -using namespace serialize19; - -TEST(serialize, BufferSlice) { - uint8_t data[] = "Hello"; - using T = BufferSlice; - auto input = T{data, sizeof(data)}; - - auto buffer = dynamicWrite(input); - - EXPECT_EQ(buffer.size(), sizeof(data) + sizeof(uint32_t)); - - auto reader = ReadArchive{buffer.slice()}; - auto output = T{}; - serialize(reader, output); - - EXPECT_EQ(output, input); -} +#include +#include +#include +#include +#include +#include + +using namespace serialize19; + +TEST(serialize, BufferSpan) { + uint8_t data[] = "Hello"; + using T = BufferSpan; + auto input = T{data, sizeof(data)}; + + auto buffer = dynamicWrite(input); + + EXPECT_EQ(buffer.size(), sizeof(data) + sizeof(uint32_t)); + + auto reader = ReadArchive{buffer.span()}; + auto output = T{}; + serialize(reader, output); + + EXPECT_EQ(output, input); +} diff --git a/src/serialize19.lib/serialize19/serialize.DynamicArrayOf.h b/src/serialize19.lib/serialize19/serialize.DynamicArray.h similarity index 76% rename from src/serialize19.lib/serialize19/serialize.DynamicArrayOf.h rename to src/serialize19.lib/serialize19/serialize.DynamicArray.h index c63922f0..0551a711 100644 --- a/src/serialize19.lib/serialize19/serialize.DynamicArrayOf.h +++ b/src/serialize19.lib/serialize19/serialize.DynamicArray.h @@ -1,28 +1,28 @@ -#pragma once -#include "array19/DynamicArrayOf.h" -#include "serialize.h" - -#include // uint32_t -#include - -namespace serialize19 { - -using array19::DynamicArrayOf; - -template void serialize(A& a, DynamicArrayOf& dynArray) { - auto count = static_cast(dynArray.count()); - serialize(a, count); - if constexpr (A::mode == ArchiveMode::Read) { - dynArray.ensureCapacity(count); - for (auto i = 0U; i < count; i++) { - auto value = T{}; - serialize(a, value); - dynArray.emplace_back(std::move(value)); - } - } - else { - for (auto& value : dynArray) serialize(a, value); - } -} - -} // namespace serialize19 +#pragma once +#include "array19/DynamicArray.h" +#include "serialize.h" + +#include // uint32_t +#include + +namespace serialize19 { + +using array19::DynamicArray; + +template void serialize(A& a, DynamicArray& dynArray) { + auto count = static_cast(dynArray.count()); + serialize(a, count); + if constexpr (A::mode == ArchiveMode::Read) { + dynArray.ensureCapacity(count); + for (auto i = 0U; i < count; i++) { + auto value = T{}; + serialize(a, value); + dynArray.emplace_back(std::move(value)); + } + } + else { + for (auto& value : dynArray) serialize(a, value); + } +} + +} // namespace serialize19 diff --git a/src/serialize19.lib/serialize19/serialize.DynamicArrayOf.test.cpp b/src/serialize19.lib/serialize19/serialize.DynamicArray.test.cpp similarity index 55% rename from src/serialize19.lib/serialize19/serialize.DynamicArrayOf.test.cpp rename to src/serialize19.lib/serialize19/serialize.DynamicArray.test.cpp index 3650cd13..9cabb6de 100644 --- a/src/serialize19.lib/serialize19/serialize.DynamicArrayOf.test.cpp +++ b/src/serialize19.lib/serialize19/serialize.DynamicArray.test.cpp @@ -1,34 +1,34 @@ -#include -#include -#include -#include -#include -#include - -using namespace serialize19; - -TEST(serialize, DynamicArrayOf) { - using T = array19::DynamicArrayOf; - auto input = T{7, 13, 23}; - - auto buffer = dynamicWrite(input); - - auto reader = ReadArchive{buffer.slice()}; - auto output = T{}; - serialize(reader, output); - - EXPECT_EQ(output, input); -} - -TEST(serialize, DynamicArrayOf_empty) { - using T = array19::DynamicArrayOf; - auto input = T{}; - - auto buffer = dynamicWrite(input); - - auto reader = ReadArchive{buffer.slice()}; - auto output = T{}; - serialize(reader, output); - - EXPECT_EQ(output, input); -} +#include +#include +#include +#include +#include +#include + +using namespace serialize19; + +TEST(serialize, DynamicArray) { + using T = array19::DynamicArray; + auto input = T{7, 13, 23}; + + auto buffer = dynamicWrite(input); + + auto reader = ReadArchive{buffer.span()}; + auto output = T{}; + serialize(reader, output); + + EXPECT_EQ(output, input); +} + +TEST(serialize, DynamicArrayOf_empty) { + using T = array19::DynamicArray; + auto input = T{}; + + auto buffer = dynamicWrite(input); + + auto reader = ReadArchive{buffer.span()}; + auto output = T{}; + serialize(reader, output); + + EXPECT_EQ(output, input); +} diff --git a/src/serialize19.lib/serialize19/serialize.Optional.test.cpp b/src/serialize19.lib/serialize19/serialize.Optional.test.cpp index e24c1cb7..f5d4287e 100644 --- a/src/serialize19.lib/serialize19/serialize.Optional.test.cpp +++ b/src/serialize19.lib/serialize19/serialize.Optional.test.cpp @@ -17,7 +17,7 @@ TEST(serialize, Optional_value) { ASSERT_EQ(buffer.size(), 1 + sizeof(int)); // contains just a bool + int - auto reader = ReadArchive{buffer.slice()}; + auto reader = ReadArchive{buffer.span()}; auto output = T{}; serialize(reader, output); @@ -32,7 +32,7 @@ TEST(serialize, Optional_empty) { ASSERT_EQ(buffer.size(), 1u); // contains just a bool - auto reader = ReadArchive{buffer.slice()}; + auto reader = ReadArchive{buffer.span()}; auto output = T{}; serialize(reader, output); diff --git a/src/serialize19.lib/serialize19/serialize.PackedOptional.test.cpp b/src/serialize19.lib/serialize19/serialize.PackedOptional.test.cpp index 4bcf5b73..cc39903f 100644 --- a/src/serialize19.lib/serialize19/serialize.PackedOptional.test.cpp +++ b/src/serialize19.lib/serialize19/serialize.PackedOptional.test.cpp @@ -20,7 +20,7 @@ TEST(serialize, PackedOptional_value) { ASSERT_EQ(buffer.size(), sizeof(int)); // contains just the int - auto reader = ReadArchive{buffer.slice()}; + auto reader = ReadArchive{buffer.span()}; auto output = T{}; serialize(reader, output); @@ -35,7 +35,7 @@ TEST(serialize, PackedOptional_default) { ASSERT_EQ(buffer.size(), sizeof(int)); // contains just the int - auto reader = ReadArchive{buffer.slice()}; + auto reader = ReadArchive{buffer.span()}; auto output = T{}; serialize(reader, output); @@ -50,7 +50,7 @@ TEST(serialize, PackedOptionalNan_value) { ASSERT_EQ(buffer.size(), sizeof(double)); // contains just the int - auto reader = ReadArchive{buffer.slice()}; + auto reader = ReadArchive{buffer.span()}; auto output = T{}; serialize(reader, output); @@ -65,7 +65,7 @@ TEST(serialize, PackedOptionalNan_default) { ASSERT_EQ(buffer.size(), sizeof(double)); // contains just the int - auto reader = ReadArchive{buffer.slice()}; + auto reader = ReadArchive{buffer.span()}; auto output = T{}; serialize(reader, output); diff --git a/src/serialize19.lib/serialize19/serialize.Partial.test.cpp b/src/serialize19.lib/serialize19/serialize.Partial.test.cpp index b98229dc..70431847 100644 --- a/src/serialize19.lib/serialize19/serialize.Partial.test.cpp +++ b/src/serialize19.lib/serialize19/serialize.Partial.test.cpp @@ -16,7 +16,7 @@ TEST(serialize, Partial) { auto buffer = dynamicWrite(input); - auto reader = ReadArchive{buffer.slice()}; + auto reader = ReadArchive{buffer.span()}; auto output = T{}; serialize(reader, output); diff --git a/src/serialize19.lib/serialize19/serialize.Tuple.test.cpp b/src/serialize19.lib/serialize19/serialize.Tuple.test.cpp index 50acb654..0a95fc6e 100644 --- a/src/serialize19.lib/serialize19/serialize.Tuple.test.cpp +++ b/src/serialize19.lib/serialize19/serialize.Tuple.test.cpp @@ -15,7 +15,7 @@ TEST(serialize, Tuple) { auto buffer = dynamicWrite(input); - auto reader = ReadArchive{buffer.slice()}; + auto reader = ReadArchive{buffer.span()}; auto output = T{}; serialize(reader, output); diff --git a/src/serialize19.lib/serialize19/serialize.Variant.test.cpp b/src/serialize19.lib/serialize19/serialize.Variant.test.cpp index 7f16b4d5..6f3ca95d 100644 --- a/src/serialize19.lib/serialize19/serialize.Variant.test.cpp +++ b/src/serialize19.lib/serialize19/serialize.Variant.test.cpp @@ -17,7 +17,7 @@ TEST(serialize, Variant) { auto buffer = dynamicWrite(input); - auto reader = ReadArchive{buffer.slice()}; + auto reader = ReadArchive{buffer.span()}; auto output = T{}; serialize(reader, output); diff --git a/src/serialize19.lib/serialize19/serialize.std_bitset.test.cpp b/src/serialize19.lib/serialize19/serialize.std_bitset.test.cpp index 2a8f65b2..fe6f1a0b 100644 --- a/src/serialize19.lib/serialize19/serialize.std_bitset.test.cpp +++ b/src/serialize19.lib/serialize19/serialize.std_bitset.test.cpp @@ -13,7 +13,7 @@ TEST(serialize, std_bitset23) { auto buffer = dynamicWrite(input); - auto reader = ReadArchive{buffer.slice()}; + auto reader = ReadArchive{buffer.span()}; auto output = T{}; serialize(reader, output); @@ -27,7 +27,7 @@ TEST(serialize, std_bitset24) { auto buffer = dynamicWrite(input); - auto reader = ReadArchive{buffer.slice()}; + auto reader = ReadArchive{buffer.span()}; auto output = T{}; serialize(reader, output); diff --git a/src/serialize19.lib/serialize19/serialize.std_optional.test.cpp b/src/serialize19.lib/serialize19/serialize.std_optional.test.cpp index 6c341fac..aea28b67 100644 --- a/src/serialize19.lib/serialize19/serialize.std_optional.test.cpp +++ b/src/serialize19.lib/serialize19/serialize.std_optional.test.cpp @@ -14,7 +14,7 @@ TEST(serialize, std_optional_value) { ASSERT_EQ(buffer.size(), 1 + sizeof(int)); // contains just a bool + int - auto reader = ReadArchive{buffer.slice()}; + auto reader = ReadArchive{buffer.span()}; auto output = T{}; serialize(reader, output); @@ -29,7 +29,7 @@ TEST(serialize, std_optional_empty) { ASSERT_EQ(buffer.size(), 1u); // contains just a bool - auto reader = ReadArchive{buffer.slice()}; + auto reader = ReadArchive{buffer.span()}; auto output = T{}; serialize(reader, output); diff --git a/src/serialize19.lib/serialize19/serialize.std_tuple.test.cpp b/src/serialize19.lib/serialize19/serialize.std_tuple.test.cpp index f6b86e26..efb91656 100644 --- a/src/serialize19.lib/serialize19/serialize.std_tuple.test.cpp +++ b/src/serialize19.lib/serialize19/serialize.std_tuple.test.cpp @@ -13,7 +13,7 @@ TEST(serialize, std_tuple) { auto buffer = dynamicWrite(input); - auto reader = ReadArchive{buffer.slice()}; + auto reader = ReadArchive{buffer.span()}; auto output = T{}; serialize(reader, output); diff --git a/src/serialize19.lib/serialize19/serialize.std_variant.test.cpp b/src/serialize19.lib/serialize19/serialize.std_variant.test.cpp index 11379e14..ae7fa74e 100644 --- a/src/serialize19.lib/serialize19/serialize.std_variant.test.cpp +++ b/src/serialize19.lib/serialize19/serialize.std_variant.test.cpp @@ -14,7 +14,7 @@ TEST(serialize, std_variant) { auto buffer = dynamicWrite(input); - auto reader = ReadArchive{buffer.slice()}; + auto reader = ReadArchive{buffer.span()}; auto output = T{}; serialize(reader, output); diff --git a/src/serialize19.lib/serialize19/serialize.std_vector.h b/src/serialize19.lib/serialize19/serialize.std_vector.h index ba3d1b0a..59af13f0 100644 --- a/src/serialize19.lib/serialize19/serialize.std_vector.h +++ b/src/serialize19.lib/serialize19/serialize.std_vector.h @@ -15,7 +15,7 @@ template void serialize(A& a, std::vector allows no byte level access if constexpr (A::mode == ArchiveMode::Size) { auto byteCount = (size + 7u) >> 3; - a.withSlice(BufferSlice{nullptr, byteCount}); + a.withSpan(BufferSpan{nullptr, byteCount}); } else if constexpr (A::mode == ArchiveMode::Read) { auto it = vector.begin(); diff --git a/src/serialize19.lib/serialize19/serialize.std_vector.test.cpp b/src/serialize19.lib/serialize19/serialize.std_vector.test.cpp index 8b93b489..ce5d816b 100644 --- a/src/serialize19.lib/serialize19/serialize.std_vector.test.cpp +++ b/src/serialize19.lib/serialize19/serialize.std_vector.test.cpp @@ -12,7 +12,7 @@ TEST(serialize, std_vector) { auto buffer = dynamicWrite(input); - auto reader = ReadArchive{buffer.slice()}; + auto reader = ReadArchive{buffer.span()}; auto output = T{}; serialize(reader, output); @@ -25,7 +25,7 @@ TEST(serialize, std_vector_empty) { auto buffer = dynamicWrite(input); - auto reader = ReadArchive{buffer.slice()}; + auto reader = ReadArchive{buffer.span()}; auto output = T{}; serialize(reader, output); @@ -38,7 +38,7 @@ TEST(serialize, std_vector_bool) { auto buffer = dynamicWrite(input); - auto reader = ReadArchive{buffer.slice()}; + auto reader = ReadArchive{buffer.span()}; auto output = T{}; serialize(reader, output); diff --git a/src/serialize19.lib/serialize19/serialize19.qbs b/src/serialize19.lib/serialize19/serialize19.qbs index 202bd158..59da3ef2 100644 --- a/src/serialize19.lib/serialize19/serialize19.qbs +++ b/src/serialize19.lib/serialize19/serialize19.qbs @@ -28,10 +28,10 @@ Product { Group { name: "Impl" files: [ - "BufferSlice.h", + "BufferSpan.h", "Endianess.h", "SizeAppender.h", - "SliceReader.h", + "SpanReader.h", "UniqueBuffer.h", "WriteAppender.h", ] @@ -39,10 +39,10 @@ Product { Group { name: "serialize co-cpp19" files: [ - "serialize.AllocatedArrayOf.h", + "serialize.AllocatedArray.h", "serialize.Array.h", - "serialize.BufferSlice.h", - "serialize.DynamicArrayOf.h", + "serialize.BufferSpan.h", + "serialize.DynamicArray.h", "serialize.None.h", "serialize.Optional.h", "serialize.PackedOptional.h", diff --git a/src/serialize19.lib/serialize19/serialize19.tests.qbs b/src/serialize19.lib/serialize19/serialize19.tests.qbs index 1c6a20cb..4f3b798a 100644 --- a/src/serialize19.lib/serialize19/serialize19.tests.qbs +++ b/src/serialize19.lib/serialize19/serialize19.tests.qbs @@ -26,8 +26,8 @@ Application { Group { name: "co-cpp19" files: [ - "serialize.BufferSlice.test.cpp", - "serialize.DynamicArrayOf.test.cpp", + "serialize.BufferSpan.test.cpp", + "serialize.DynamicArray.test.cpp", "serialize.Optional.test.cpp", "serialize.PackedOptional.test.cpp", "serialize.Partial.test.cpp", diff --git a/src/signal19.lib/signal19/SignalWith.h b/src/signal19.lib/signal19/SignalWith.h index 26c7f17f..2156d183 100644 --- a/src/signal19.lib/signal19/SignalWith.h +++ b/src/signal19.lib/signal19/SignalWith.h @@ -1,13 +1,13 @@ #pragma once #include "Subscription.h" -#include "array19/DynamicArrayOf.h" +#include "array19/DynamicArray.h" #include #include namespace signal19 { -using array19::DynamicArrayOf; +using array19::DynamicArray; namespace details { @@ -39,7 +39,7 @@ struct TrackingCancellation : Cancellation { template struct SignalWith final : details::Signal { private: struct StoredCancellation; - using Vec = DynamicArrayOf; + using Vec = DynamicArray; /// partial implementation that implements the cancellation /// This also is the interface for the internal type erasure. @@ -50,7 +50,7 @@ template struct SignalWith final : details::Signal { // Cancellation interface void cancel() noexcept final override { - auto it = std::find_if(forSignal.vec.amendBegin(), forSignal.vec.amendEnd(), [this](auto& ptr) { + auto it = std::find_if(forSignal.vec.amend().begin(), forSignal.vec.amend().end(), [this](auto& ptr) { return &ptr.m() == this; }); forSignal.vec.remove(it, 1); diff --git a/src/signal19.lib/signal19/Subscriptions.h b/src/signal19.lib/signal19/Subscriptions.h index 7759fa2d..b7f7338c 100644 --- a/src/signal19.lib/signal19/Subscriptions.h +++ b/src/signal19.lib/signal19/Subscriptions.h @@ -1,20 +1,20 @@ #pragma once #include "Subscription.h" -#include -#include +#include +#include #include namespace signal19 { -using array19::DynamicArrayOf; -using array19::moveSliceOfSingle; +using array19::DynamicArray; +using array19::moveSpanOne; /// Holds many subscriptions /// /// useful if a single entity subscribes to multiple signals. struct Subscriptions { - using Array = DynamicArrayOf; + using Array = DynamicArray; private: Array m_array; @@ -34,7 +34,7 @@ struct Subscriptions { /// /// note: /// * does NOT check for duplicates (subscription is already done) - auto add(Subscription&& subscription) { m_array.appendMoved(moveSliceOfSingle(std::move(subscription))); } + auto add(Subscription&& subscription) { m_array.appendMoved(moveSpanOne(std::move(subscription))); } /// cancels all the subscriptions ///