Skip to content

Commit dcec96f

Browse files
committed
Fix OrderedSetOf to make it ready for usage
1 parent 8fcef31 commit dcec96f

File tree

3 files changed

+332
-124
lines changed

3 files changed

+332
-124
lines changed

src/lookup19.lib/lookup19/OrderedSetOf.h

Lines changed: 152 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ template<class T, class Less = DefaultLess> struct OrderedSetOf {
2323
using Slice = OrderedSliceOf<const T, Less>;
2424
using AmendableSlice = SliceOf<T>;
2525
using UnorderedSlice = SliceOf<const T>;
26+
using MoveSlice = array19::MoveSliceOf<T>;
2627

2728
static_assert(std::is_trivial_v<T>, "Only works for trivial types!");
2829

@@ -34,39 +35,68 @@ template<class T, class Less = DefaultLess> struct OrderedSetOf {
3435
Count m_capacity{};
3536

3637
public:
37-
OrderedSetOf() = default;
38-
~OrderedSetOf() noexcept {
38+
constexpr OrderedSetOf() = default;
39+
explicit constexpr OrderedSetOf(Slice ordered) noexcept
40+
: m_pointer{Utils::allocate(ordered.count())}
41+
, m_count{ordered.count()}
42+
, m_capacity{ordered.count()} {
43+
Utils::copyAssign(m_pointer, ordered);
44+
}
45+
constexpr OrderedSetOf(const OrderedSetOf& o) noexcept
46+
: m_pointer{Utils::allocate(o.m_count)}
47+
, m_count{o.m_count}
48+
, m_capacity{o.m_count} {
49+
Utils::copyAssign(m_pointer, Slice{o});
50+
}
51+
constexpr OrderedSetOf(OrderedSetOf&& o) noexcept
52+
: m_pointer{std::exchange(o.m_pointer, nullptr)}
53+
, m_count{o.m_count}
54+
, m_capacity{o.m_count} {}
55+
constexpr auto operator=(const OrderedSetOf& o) noexcept -> OrderedSetOf& {
56+
if (o.m_count > m_capacity) {
57+
if (m_pointer) Utils::deallocate(SliceOf{m_pointer, m_capacity});
58+
m_pointer = Utils::allocate(o.m_count);
59+
m_capacity = o.m_count;
60+
}
61+
Utils::copyAssign(m_pointer, Slice{o});
62+
m_count = o.m_count;
63+
return *this;
64+
}
65+
constexpr auto operator=(OrderedSetOf&& o) noexcept -> OrderedSetOf& {
66+
if (m_pointer) Utils::deallocate(SliceOf{m_pointer, m_capacity});
67+
m_pointer = std::exchange(o.m_pointer, nullptr);
68+
m_count = o.m_count;
69+
m_capacity = o.m_capacity;
70+
return *this;
71+
}
72+
constexpr ~OrderedSetOf() noexcept {
3973
if (m_pointer) Utils::deallocate(SliceOf{m_pointer, m_capacity});
4074
}
4175

42-
[[nodiscard]] constexpr auto isEmpty() const noexcept -> bool { return m_count == 0; }
43-
[[nodiscard]] auto count() const -> Count { return m_count; }
44-
[[nodiscard]] auto totalCapacity() const -> Count { return m_capacity; }
45-
[[nodiscard]] auto unusedCapacity() const -> Count { return m_capacity - m_count; }
76+
[[nodiscard]] constexpr auto isEmpty() const -> bool { return m_count == 0; }
77+
[[nodiscard]] constexpr auto count() const -> Count { return m_count; }
78+
[[nodiscard]] constexpr auto totalCapacity() const -> Count { return m_capacity; }
79+
[[nodiscard]] constexpr auto unusedCapacity() const -> Count { return m_capacity - m_count; }
4680

47-
[[nodiscard]] auto front() const -> const T& { return *begin(); }
48-
[[nodiscard]] auto back() const -> const T& { return *(begin() + m_count - 1); }
81+
[[nodiscard]] constexpr auto front() const -> const T& { return *begin(); }
82+
[[nodiscard]] constexpr auto back() const -> const T& { return *(begin() + m_count - 1); }
4983

50-
[[nodiscard]] auto begin() const noexcept -> ConstIterator {
51-
return std::launder(reinterpret_cast<const T*>(m_pointer));
52-
}
53-
[[nodiscard]] auto end() const noexcept -> ConstIterator { return begin() + m_count; }
54-
[[nodiscard]] auto operator[](Index index) const noexcept -> const Element& {
55-
return *std::launder(reinterpret_cast<const T*>(m_pointer + index));
56-
}
57-
[[nodiscard]] operator Slice() const noexcept { return Slice{begin(), m_count}; }
84+
[[nodiscard]] constexpr auto begin() const noexcept -> ConstIterator { return m_pointer; }
85+
[[nodiscard]] constexpr auto end() const noexcept -> ConstIterator { return begin() + m_count; }
86+
[[nodiscard]] constexpr auto operator[](Index index) const -> const Element& { return m_pointer[index]; }
87+
[[nodiscard]] constexpr operator Slice() const { return Slice{m_pointer, m_count}; }
5888

59-
void ensureCapacity(Count count) {
89+
constexpr void ensureCapacity(Count count) {
6090
if (totalCapacity() < count) growBy(static_cast<size_t>(count - totalCapacity()));
6191
}
62-
void ensureUnusedCapacity(Count count) {
92+
constexpr void ensureUnusedCapacity(Count count) {
6393
if (unusedCapacity() < count) growBy(static_cast<size_t>(count - unusedCapacity()));
6494
}
6595

6696
/// inserts a single value to the set if it is not yet present
6797
/// note:
6898
/// * if you want to insert muliple values use merge
69-
bool insert(T v) {
99+
constexpr auto insert(T v) -> bool {
70100
auto it = const_cast<Iterator>(static_cast<Slice>(*this).lowerBound(v));
71101
if (it != end() && *it == v) {
72102
return false;
@@ -76,12 +106,12 @@ template<class T, class Less = DefaultLess> struct OrderedSetOf {
76106
auto nPtr = newStorage.begin();
77107
auto fCount = static_cast<size_t>(it - m_pointer);
78108
if (0 != fCount) {
79-
memcpy(nPtr, m_pointer, fCount);
109+
Utils::moveConstruct(nPtr, MoveSlice{m_pointer, fCount});
80110
nPtr += fCount;
81111
}
82112
*nPtr++ = v;
83113
if (fCount != m_count) {
84-
memcpy(nPtr, m_pointer + fCount, m_count - fCount);
114+
Utils::moveConstruct(nPtr, MoveSlice{m_pointer + fCount, m_count - fCount});
85115
}
86116
Utils::deallocate(SliceOf{m_pointer, m_capacity});
87117
m_pointer = newStorage.begin();
@@ -90,7 +120,7 @@ template<class T, class Less = DefaultLess> struct OrderedSetOf {
90120
return true;
91121
}
92122
if (it != end()) {
93-
memmove(it + 1, it, static_cast<size_t>(end() - it));
123+
Utils::moveAssignReverse(it + 1, MoveSlice{it, static_cast<size_t>(end() - it)});
94124
}
95125
*it = v;
96126
m_count++;
@@ -100,136 +130,142 @@ template<class T, class Less = DefaultLess> struct OrderedSetOf {
100130
/// removes a single element from the set
101131
/// preconditions:
102132
/// * cIt has to point between begin() and before end()
103-
void remove(ConstIterator cIt) {
133+
constexpr void remove(ConstIterator cIt) {
104134
auto it = const_cast<Iterator>(cIt);
105135
if (it + 1 != end()) {
106-
memmove(it, it + 1, static_cast<size_t>(end() - it - 1));
136+
Utils::moveAssignForward(it, MoveSlice{it + 1, static_cast<size_t>(end() - it)});
107137
}
108138
m_count--;
109139
}
110140

111141
/// adds multiple elements to the set if they are not already present
112-
/// note:
113-
/// * assumes that enough capacity for inserting all elements is required
114-
void merge(Slice elems) {
142+
/// preconditions:
143+
/// * assumes that elems are unique and ordered
144+
constexpr void merge(Slice elems) {
115145
if (elems.isEmpty()) return;
116-
auto less = Less{};
146+
if (isEmpty()) {
147+
*this = OrderedSetOf{elems};
148+
return;
149+
}
117150
auto nCount = elems.count();
118-
if (unusedCapacity() < nCount) { // merge into new storage
151+
if (unusedCapacity() < nCount) {
119152
auto newStorage = grownStorage(nCount);
120-
auto dPtr = newStorage.begin();
121-
auto nPtr = elems.begin();
122-
auto nEnd = elems.end();
123-
if (isEmpty()) {
124-
memcpy(dPtr, nPtr, nCount);
125-
Utils::deallocate(SliceOf{m_pointer, m_capacity});
126-
m_pointer = newStorage.begin();
127-
m_capacity = newStorage.count();
128-
m_count = nCount;
129-
return;
130-
}
131-
auto n = *nPtr;
132-
auto oPtr = begin();
133-
auto o = *oPtr;
134-
auto oEnd = end();
135-
while (true) {
136-
if (less(n, o)) {
137-
*dPtr++ = n;
138-
nPtr++;
139-
if (nPtr == nEnd) {
140-
memcpy(dPtr, oPtr, oEnd - oPtr);
141-
break;
142-
}
143-
n = *nPtr;
144-
}
145-
else {
146-
*dPtr++ = o;
147-
oPtr++;
148-
if (!less(o, n)) {
149-
nPtr++;
150-
if (nPtr == nEnd) {
151-
if (oPtr != oEnd) memcpy(dPtr, oPtr, oEnd - oPtr);
152-
break;
153-
}
154-
n = *nPtr;
155-
}
156-
if (oPtr == oEnd) {
157-
memcpy(dPtr, nPtr, nEnd - nPtr);
158-
break;
159-
}
160-
o = *oPtr;
161-
}
162-
}
153+
auto ptr = mergeInto(newStorage, elems);
163154
Utils::deallocate(SliceOf{m_pointer, m_capacity});
164155
m_pointer = newStorage.begin();
165156
m_capacity = newStorage.count();
166-
m_count += nCount;
167-
return;
157+
m_count = static_cast<size_t>(ptr - m_pointer);
168158
}
169-
if (isEmpty()) {
170-
memcpy(m_pointer, elems.begin(), nCount);
171-
m_count = nCount;
172-
return;
159+
else {
160+
m_count = mergeBackwards(elems);
173161
}
174-
auto oBegin = m_pointer;
175-
auto oIt = oBegin + m_count - 1;
176-
auto o = *oIt;
177-
auto nBegin = elems.begin();
162+
}
163+
164+
private:
165+
[[nodiscard]] constexpr auto grownStorage(size_t growBy) const -> AmendableSlice {
166+
auto cur = m_capacity;
167+
auto res = (cur << 1) - (cur >> 1) + (cur >> 4); // * 1.563
168+
if (res < 5) res = 5;
169+
if (res < m_capacity + growBy) res = m_capacity + growBy;
170+
auto ptr = Utils::allocate(res);
171+
return AmendableSlice{ptr, res};
172+
}
173+
constexpr void growBy(size_t by) {
174+
auto newStorage = grownStorage(by);
175+
Utils::moveConstruct(newStorage.begin(), MoveSlice{m_pointer, m_count});
176+
Utils::deallocate(SliceOf{m_pointer, m_capacity});
177+
m_pointer = newStorage.begin();
178+
m_capacity = newStorage.count();
179+
}
180+
constexpr auto mergeInto(AmendableSlice storage, Slice elems) -> T* {
181+
auto less = Less{};
182+
auto dPtr = storage.begin();
183+
auto nPtr = elems.begin();
184+
auto const nEnd = elems.end();
185+
auto n = *nPtr;
186+
auto oPtr = begin();
187+
auto o = *oPtr;
188+
auto const oEnd = end();
189+
while (true) {
190+
if (less(n, o)) {
191+
*dPtr++ = n;
192+
nPtr++;
193+
if (nPtr == nEnd) {
194+
auto r = static_cast<size_t>(oEnd - oPtr);
195+
Utils::copyAssign(dPtr, UnorderedSlice{oPtr, r});
196+
return dPtr + r;
197+
}
198+
n = *nPtr;
199+
}
200+
else {
201+
if (less(o, n)) {
202+
*dPtr++ = o;
203+
}
204+
oPtr++;
205+
if (oPtr == oEnd) {
206+
auto r = static_cast<size_t>(nEnd - nPtr);
207+
Utils::copyAssign(dPtr, UnorderedSlice{nPtr, r});
208+
return dPtr + r;
209+
}
210+
o = *oPtr;
211+
}
212+
}
213+
}
214+
constexpr auto mergeBackwards(Slice elems) -> size_t {
215+
auto less = Less{};
216+
auto const oBegin = m_pointer;
217+
auto const oCount = m_count;
218+
auto const nBegin = elems.begin();
219+
auto const nCount = elems.count();
220+
auto oIt = oBegin + oCount - 1;
178221
auto nIt = nBegin + nCount - 1;
179-
auto n = *nIt;
180-
auto dIt = oBegin + m_count + nCount - 1;
222+
auto dCount = oCount + nCount;
223+
auto dIt = oBegin + dCount - 1;
181224

225+
auto o = *oIt;
226+
auto n = *nIt;
182227
while (true) {
183228
if (less(o, n)) {
184229
*dIt-- = n;
185-
nIt--;
186230
if (nIt == nBegin) {
231+
dIt++;
232+
oIt++;
233+
if (oIt != dIt) { // move by skipped elements
234+
dCount -= (dIt - oIt);
235+
Utils::moveAssignForward(oIt, MoveSlice{dIt, static_cast<size_t>(dCount - (oIt - oBegin))});
236+
}
187237
// remaining old values are in place
188-
break;
238+
return dCount;
189239
}
240+
nIt--;
241+
n = *nIt;
190242
}
191243
else {
192-
*dIt-- = o;
193-
oIt--;
194-
if (!less(n, o)) {
195-
nIt--;
196-
if (nIt == nBegin) {
197-
// remaining old values are in place
198-
break;
199-
}
200-
n = *nIt;
244+
if (less(n, o)) {
245+
*dIt-- = o;
201246
}
202247
if (oIt == oBegin) {
203-
memcpy(oBegin, nBegin, nIt - nBegin);
204-
break;
248+
auto const remaining = static_cast<size_t>(nIt + 1 - nBegin);
249+
Utils::copyAssign(oBegin, UnorderedSlice{nBegin, remaining});
250+
dIt++;
251+
if (oBegin + remaining != dIt) { // move by skipped elements
252+
dCount -= (dIt - (oBegin + remaining));
253+
Utils::moveAssignForward(
254+
oBegin + remaining,
255+
MoveSlice{dIt, static_cast<size_t>(dCount - remaining)});
256+
}
257+
return dCount;
205258
}
259+
oIt--;
206260
o = *oIt;
207261
}
208262
}
209-
m_count += nCount;
210-
}
211-
212-
private:
213-
[[nodiscard]] auto grownStorage(size_t growBy) const -> AmendableSlice {
214-
auto cur = m_capacity;
215-
auto res = (cur << 1) - (cur >> 1) + (cur >> 4); // * 1.563
216-
if (res < 5) res = 5;
217-
if (res < m_capacity + growBy) res = m_capacity + growBy;
218-
auto ptr = Utils::allocate(res);
219-
return AmendableSlice{ptr, res};
220-
}
221-
void growBy(size_t by) {
222-
auto newStorage = grownStorage(by);
223-
memcpy(newStorage.begin(), m_pointer, m_count);
224-
Utils::deallocate(SliceOf{m_pointer, m_capacity});
225-
m_pointer = newStorage.begin();
226-
m_capacity = newStorage.count();
227263
}
228264
};
229265

230266
/// deduce OrderedSliceOf from OrderedSetOf
231267
/// usage:
232-
/// auto a = OrderedSetOf{1,2,3;
268+
/// auto a = OrderedSetOf{1,2,3};
233269
/// auto slice = OrderedSliceOf{a};
234270
template<class T, class L> OrderedSliceOf(const OrderedSetOf<T, L>&) -> OrderedSliceOf<const T, L>;
235271

0 commit comments

Comments
 (0)