Skip to content

Commit 0d88e17

Browse files
committed
Merge pull request godotengine#105629 from aaronp64/list_sort
Reuse and optimize sorting logic for `List`, `SelfList`, and `HashMap`
2 parents 5439b0d + 6b2674f commit 0d88e17

File tree

10 files changed

+311
-192
lines changed

10 files changed

+311
-192
lines changed

core/templates/hash_map.h

Lines changed: 12 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include "core/os/memory.h"
3434
#include "core/templates/hashfuncs.h"
3535
#include "core/templates/pair.h"
36+
#include "core/templates/sort_list.h"
3637

3738
#include <initializer_list>
3839

@@ -60,8 +61,6 @@ struct HashMapElement {
6061
data(p_key, p_value) {}
6162
};
6263

63-
bool _hashmap_variant_less_than(const Variant &p_left, const Variant &p_right);
64-
6564
template <typename TKey, typename TValue,
6665
typename Hasher = HashMapHasherDefault,
6766
typename Comparator = HashMapComparatorDefault<TKey>,
@@ -265,44 +264,18 @@ class HashMap : private Allocator {
265264
}
266265

267266
void sort() {
268-
if (elements == nullptr || num_elements < 2) {
269-
return; // An empty or single element HashMap is already sorted.
270-
}
271-
// Use insertion sort because we want this operation to be fast for the
272-
// common case where the input is already sorted or nearly sorted.
273-
HashMapElement<TKey, TValue> *inserting = head_element->next;
274-
while (inserting != nullptr) {
275-
HashMapElement<TKey, TValue> *after = nullptr;
276-
for (HashMapElement<TKey, TValue> *current = inserting->prev; current != nullptr; current = current->prev) {
277-
if (_hashmap_variant_less_than(inserting->data.key, current->data.key)) {
278-
after = current;
279-
} else {
280-
break;
281-
}
282-
}
283-
HashMapElement<TKey, TValue> *next = inserting->next;
284-
if (after != nullptr) {
285-
// Modify the elements around `inserting` to remove it from its current position.
286-
inserting->prev->next = next;
287-
if (next == nullptr) {
288-
tail_element = inserting->prev;
289-
} else {
290-
next->prev = inserting->prev;
291-
}
292-
// Modify `before` and `after` to insert `inserting` between them.
293-
HashMapElement<TKey, TValue> *before = after->prev;
294-
if (before == nullptr) {
295-
head_element = inserting;
296-
} else {
297-
before->next = inserting;
298-
}
299-
after->prev = inserting;
300-
// Point `inserting` to its new surroundings.
301-
inserting->prev = before;
302-
inserting->next = after;
303-
}
304-
inserting = next;
267+
sort_custom<KeyValueSort<TKey, TValue>>();
268+
}
269+
270+
template <typename C>
271+
void sort_custom() {
272+
if (size() < 2) {
273+
return;
305274
}
275+
276+
using E = HashMapElement<TKey, TValue>;
277+
SortList<E, KeyValue<TKey, TValue>, &E::data, &E::prev, &E::next, C> sorter;
278+
sorter.sort(head_element, tail_element);
306279
}
307280

308281
TValue &get(const TKey &p_key) {

core/templates/list.h

Lines changed: 4 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232

3333
#include "core/error/error_macros.h"
3434
#include "core/os/memory.h"
35-
#include "core/templates/sort_array.h"
35+
#include "core/templates/sort_list.h"
3636

3737
#include <initializer_list>
3838

@@ -656,104 +656,18 @@ class List {
656656
where->prev_ptr = value;
657657
}
658658

659-
/**
660-
* simple insertion sort
661-
*/
662-
663659
void sort() {
664660
sort_custom<Comparator<T>>();
665661
}
666662

667-
template <typename C>
668-
void sort_custom_inplace() {
669-
if (size() < 2) {
670-
return;
671-
}
672-
673-
Element *from = front();
674-
Element *current = from;
675-
Element *to = from;
676-
677-
while (current) {
678-
Element *next = current->next_ptr;
679-
680-
if (from != current) {
681-
current->prev_ptr = nullptr;
682-
current->next_ptr = from;
683-
684-
Element *find = from;
685-
C less;
686-
while (find && less(find->value, current->value)) {
687-
current->prev_ptr = find;
688-
current->next_ptr = find->next_ptr;
689-
find = find->next_ptr;
690-
}
691-
692-
if (current->prev_ptr) {
693-
current->prev_ptr->next_ptr = current;
694-
} else {
695-
from = current;
696-
}
697-
698-
if (current->next_ptr) {
699-
current->next_ptr->prev_ptr = current;
700-
} else {
701-
to = current;
702-
}
703-
} else {
704-
current->prev_ptr = nullptr;
705-
current->next_ptr = nullptr;
706-
}
707-
708-
current = next;
709-
}
710-
_data->first = from;
711-
_data->last = to;
712-
}
713-
714-
template <typename C>
715-
struct AuxiliaryComparator {
716-
C compare;
717-
_FORCE_INLINE_ bool operator()(const Element *a, const Element *b) const {
718-
return compare(a->value, b->value);
719-
}
720-
};
721-
722663
template <typename C>
723664
void sort_custom() {
724-
//this version uses auxiliary memory for speed.
725-
//if you don't want to use auxiliary memory, use the in_place version
726-
727-
int s = size();
728-
if (s < 2) {
665+
if (size() < 2) {
729666
return;
730667
}
731668

732-
Element **aux_buffer = memnew_arr(Element *, s);
733-
734-
int idx = 0;
735-
for (Element *E = front(); E; E = E->next_ptr) {
736-
aux_buffer[idx] = E;
737-
idx++;
738-
}
739-
740-
SortArray<Element *, AuxiliaryComparator<C>> sort;
741-
sort.sort(aux_buffer, s);
742-
743-
_data->first = aux_buffer[0];
744-
aux_buffer[0]->prev_ptr = nullptr;
745-
aux_buffer[0]->next_ptr = aux_buffer[1];
746-
747-
_data->last = aux_buffer[s - 1];
748-
aux_buffer[s - 1]->prev_ptr = aux_buffer[s - 2];
749-
aux_buffer[s - 1]->next_ptr = nullptr;
750-
751-
for (int i = 1; i < s - 1; i++) {
752-
aux_buffer[i]->prev_ptr = aux_buffer[i - 1];
753-
aux_buffer[i]->next_ptr = aux_buffer[i + 1];
754-
}
755-
756-
memdelete_arr(aux_buffer);
669+
SortList<Element, T, &Element::value, &Element::prev_ptr, &Element::next_ptr, C> sorter;
670+
sorter.sort(_data->first, _data->last);
757671
}
758672

759673
const void *id() const {

core/templates/self_list.h

Lines changed: 8 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#pragma once
3232

3333
#include "core/error/error_macros.h"
34+
#include "core/templates/sort_list.h"
3435
#include "core/typedefs.h"
3536

3637
template <typename T>
@@ -114,45 +115,13 @@ class SelfList {
114115
return;
115116
}
116117

117-
SelfList<T> *from = _first;
118-
SelfList<T> *current = from;
119-
SelfList<T> *to = from;
120-
121-
while (current) {
122-
SelfList<T> *next = current->_next;
123-
124-
if (from != current) {
125-
current->_prev = nullptr;
126-
current->_next = from;
127-
128-
SelfList<T> *find = from;
129-
C less;
130-
while (find && less(*find->_self, *current->_self)) {
131-
current->_prev = find;
132-
current->_next = find->_next;
133-
find = find->_next;
134-
}
135-
136-
if (current->_prev) {
137-
current->_prev->_next = current;
138-
} else {
139-
from = current;
140-
}
141-
142-
if (current->_next) {
143-
current->_next->_prev = current;
144-
} else {
145-
to = current;
146-
}
147-
} else {
148-
current->_prev = nullptr;
149-
current->_next = nullptr;
150-
}
151-
152-
current = next;
153-
}
154-
_first = from;
155-
_last = to;
118+
struct PtrComparator {
119+
C compare;
120+
_FORCE_INLINE_ bool operator()(const T *p_a, const T *p_b) const { return compare(*p_a, *p_b); }
121+
};
122+
using Element = SelfList<T>;
123+
SortList<Element, T *, &Element::_self, &Element::_prev, &Element::_next, PtrComparator> sorter;
124+
sorter.sort(_first, _last);
156125
}
157126

158127
_FORCE_INLINE_ SelfList<T> *first() { return _first; }

core/templates/sort_list.h

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
/**************************************************************************/
2+
/* sort_list.h */
3+
/**************************************************************************/
4+
/* This file is part of: */
5+
/* GODOT ENGINE */
6+
/* https://godotengine.org */
7+
/**************************************************************************/
8+
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9+
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10+
/* */
11+
/* Permission is hereby granted, free of charge, to any person obtaining */
12+
/* a copy of this software and associated documentation files (the */
13+
/* "Software"), to deal in the Software without restriction, including */
14+
/* without limitation the rights to use, copy, modify, merge, publish, */
15+
/* distribute, sublicense, and/or sell copies of the Software, and to */
16+
/* permit persons to whom the Software is furnished to do so, subject to */
17+
/* the following conditions: */
18+
/* */
19+
/* The above copyright notice and this permission notice shall be */
20+
/* included in all copies or substantial portions of the Software. */
21+
/* */
22+
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23+
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24+
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25+
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26+
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27+
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28+
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29+
/**************************************************************************/
30+
31+
#pragma once
32+
33+
#include "core/typedefs.h"
34+
35+
template <typename Element, typename T, T Element::*value, Element *Element::*prev, Element *Element::*next, typename Comparator = Comparator<T>>
36+
class SortList {
37+
public:
38+
Comparator compare;
39+
40+
void sort(Element *&r_head, Element *&r_tail) {
41+
Element *sorted_until;
42+
if (_is_sorted(r_head, r_tail, sorted_until)) {
43+
return;
44+
}
45+
46+
// In case we're sorting only part of a larger list.
47+
Element *head_prev = r_head->*prev;
48+
r_head->*prev = nullptr;
49+
Element *tail_next = r_tail->*next;
50+
r_tail->*next = nullptr;
51+
52+
// Sort unsorted section and merge.
53+
Element *head2 = sorted_until->*next;
54+
_split(sorted_until, head2);
55+
_merge_sort(head2, r_tail);
56+
_merge(r_head, sorted_until, head2, r_tail, r_head, r_tail);
57+
58+
// Reconnect to larger list if needed.
59+
if (head_prev) {
60+
_connect(head_prev, r_head);
61+
}
62+
if (tail_next) {
63+
_connect(r_tail, tail_next);
64+
}
65+
}
66+
67+
private:
68+
bool _is_sorted(Element *p_head, Element *p_tail, Element *&r_sorted_until) {
69+
r_sorted_until = p_head;
70+
while (r_sorted_until != p_tail) {
71+
if (compare(r_sorted_until->*next->*value, r_sorted_until->*value)) {
72+
return false;
73+
}
74+
75+
r_sorted_until = r_sorted_until->*next;
76+
}
77+
78+
return true;
79+
}
80+
81+
void _merge_sort(Element *&r_head, Element *&r_tail) {
82+
if (r_head == r_tail) {
83+
return;
84+
}
85+
86+
Element *tail1 = _get_mid(r_head);
87+
Element *head2 = tail1->*next;
88+
_split(tail1, head2);
89+
90+
_merge_sort(r_head, tail1);
91+
_merge_sort(head2, r_tail);
92+
_merge(r_head, tail1, head2, r_tail, r_head, r_tail);
93+
}
94+
95+
void _merge(
96+
Element *p_head1, Element *p_tail1,
97+
Element *p_head2, Element *p_tail2,
98+
Element *&r_head, Element *&r_tail) {
99+
if (compare(p_head2->*value, p_head1->*value)) {
100+
r_head = p_head2;
101+
p_head2 = p_head2->*next;
102+
} else {
103+
r_head = p_head1;
104+
p_head1 = p_head1->*next;
105+
}
106+
107+
Element *curr = r_head;
108+
while (p_head1 && p_head2) {
109+
if (compare(p_head2->*value, p_head1->*value)) {
110+
_connect(curr, p_head2);
111+
p_head2 = p_head2->*next;
112+
} else {
113+
_connect(curr, p_head1);
114+
p_head1 = p_head1->*next;
115+
}
116+
curr = curr->*next;
117+
}
118+
119+
if (p_head1) {
120+
_connect(curr, p_head1);
121+
r_tail = p_tail1;
122+
} else {
123+
_connect(curr, p_head2);
124+
r_tail = p_tail2;
125+
}
126+
}
127+
128+
Element *_get_mid(Element *p_head) {
129+
Element *end = p_head;
130+
Element *mid = p_head;
131+
while (end->*next && end->*next->*next) {
132+
end = end->*next->*next;
133+
mid = mid->*next;
134+
}
135+
136+
return mid;
137+
}
138+
139+
_FORCE_INLINE_ void _connect(Element *p_a, Element *p_b) {
140+
p_a->*next = p_b;
141+
p_b->*prev = p_a;
142+
}
143+
144+
_FORCE_INLINE_ void _split(Element *p_a, Element *p_b) {
145+
p_a->*next = nullptr;
146+
p_b->*prev = nullptr;
147+
}
148+
};

0 commit comments

Comments
 (0)