Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/Common/Data/Collections/TinySet.h
3189 views
1
#pragma once
2
3
#include <vector>
4
5
#include "Common/Log.h"
6
7
// Insert-only small-set implementation. Performs no allocation unless MaxFastSize is exceeded.
8
// Can also be used as a small vector, then use push_back (or push_in_place) instead of insert.
9
// Duplicates are thus allowed if you use that, but not if you exclusively use insert.
10
template <class T, int MaxFastSize>
11
struct TinySet {
12
~TinySet() { delete slowLookup_; }
13
inline void insert(const T &t) {
14
// Fast linear scan.
15
for (int i = 0; i < fastCount_; i++) {
16
if (fastLookup_[i] == t)
17
return; // We already have it.
18
}
19
// Fast insertion
20
if (fastCount_ < MaxFastSize) {
21
fastLookup_[fastCount_++] = t;
22
return;
23
}
24
// Fall back to slow path.
25
insertSlow(t);
26
}
27
inline void push_back(const T &t) {
28
if (fastCount_ < MaxFastSize) {
29
fastLookup_[fastCount_++] = t;
30
return;
31
}
32
if (!slowLookup_) {
33
slowLookup_ = new std::vector<T>();
34
}
35
slowLookup_->push_back(t);
36
}
37
inline T &push_uninitialized() {
38
if (fastCount_ < MaxFastSize) {
39
return fastLookup_[fastCount_++];
40
}
41
if (!slowLookup_) {
42
slowLookup_ = new std::vector<T>();
43
}
44
45
// The slow lookup is also slow at adding.
46
T t;
47
slowLookup_->push_back(t);
48
return *slowLookup_->back();
49
}
50
void reserve(size_t size) {
51
if (size < MaxFastSize) {
52
return;
53
}
54
if (slowLookup_) {
55
if (size < MaxFastSize + slowLookup_->capacity()) {
56
return;
57
}
58
slowLookup_->reserve(size - MaxFastSize);
59
} else {
60
slowLookup_ = new std::vector<T>();
61
slowLookup_->reserve(size - MaxFastSize);
62
}
63
}
64
void append(const TinySet<T, MaxFastSize> &other) {
65
size_t otherSize = other.size();
66
if (size() + otherSize <= MaxFastSize) {
67
// Fast case
68
for (size_t i = 0; i < otherSize; i++) {
69
fastLookup_[fastCount_ + i] = other.fastLookup_[i];
70
}
71
fastCount_ += other.fastCount_;
72
} else {
73
for (size_t i = 0; i < otherSize; i++) {
74
push_back(other[i]);
75
}
76
}
77
}
78
bool contains(T t) const {
79
for (int i = 0; i < fastCount_; i++) {
80
if (fastLookup_[i] == t)
81
return true;
82
}
83
if (slowLookup_) {
84
for (auto x : *slowLookup_) {
85
if (x == t)
86
return true;
87
}
88
}
89
return false;
90
}
91
bool contains(const TinySet<T, MaxFastSize> &otherSet) {
92
// Awkward, kind of ruins the fun.
93
for (int i = 0; i < fastCount_; i++) {
94
if (otherSet.contains(fastLookup_[i]))
95
return true;
96
}
97
if (slowLookup_) {
98
for (auto x : *slowLookup_) {
99
if (otherSet.contains(x))
100
return true;
101
}
102
}
103
return false;
104
}
105
void clear() {
106
// TODO: Keep slowLookup_ around? That would be more similar to real vector behavior.
107
delete slowLookup_;
108
slowLookup_ = nullptr;
109
fastCount_ = 0;
110
}
111
bool empty() const {
112
return fastCount_ == 0;
113
}
114
size_t size() const {
115
if (!slowLookup_) {
116
return fastCount_;
117
} else {
118
return slowLookup_->size() + MaxFastSize;
119
}
120
}
121
T &operator[] (size_t index) {
122
if (index < MaxFastSize) {
123
return fastLookup_[index];
124
} else {
125
return (*slowLookup_)[index - MaxFastSize];
126
}
127
}
128
const T &operator[] (size_t index) const {
129
if (index < MaxFastSize) {
130
return fastLookup_[index];
131
} else {
132
return (*slowLookup_)[index - MaxFastSize];
133
}
134
}
135
const T &back() const {
136
return (*this)[size() - 1];
137
}
138
139
private:
140
void insertSlow(T t) {
141
if (!slowLookup_) {
142
slowLookup_ = new std::vector<T>();
143
} else {
144
for (size_t i = 0; i < slowLookup_->size(); i++) {
145
if ((*slowLookup_)[i] == t)
146
return;
147
}
148
}
149
slowLookup_->push_back(t);
150
}
151
int fastCount_ = 0; // first in the struct just so it's more visible in the VS debugger.
152
T fastLookup_[MaxFastSize];
153
std::vector<T> *slowLookup_ = nullptr;
154
};
155
156
template <class T, int MaxSize>
157
struct FixedVec {
158
~FixedVec() {}
159
// WARNING: Can fail if you exceed MaxSize!
160
inline bool push_back(const T &t) {
161
if (count_ < MaxSize) {
162
data_[count_++] = t;
163
return true;
164
} else {
165
return false;
166
}
167
}
168
169
// WARNING: Can fail if you exceed MaxSize!
170
inline T &push_uninitialized() {
171
if (count_ < MaxSize) {
172
return &data_[count_++];
173
}
174
_dbg_assert_(false);
175
return *data_[MaxSize - 1]; // BAD
176
}
177
178
// Invalid if empty().
179
void pop_back() { count_--; }
180
181
// Unlike TinySet, we can trivially support begin/end as pointers.
182
T *begin() { return data_; }
183
T *end() { return data_ + count_; }
184
const T *begin() const { return data_; }
185
const T *end() const { return data_ + count_; }
186
187
size_t capacity() const { return MaxSize; }
188
void clear() { count_ = 0; }
189
bool empty() const { return count_ == 0; }
190
size_t size() const { return count_; }
191
192
bool contains(T t) const {
193
for (int i = 0; i < count_; i++) {
194
if (data_[i] == t)
195
return true;
196
}
197
return false;
198
}
199
200
// Out of bounds (past size() - 1) is undefined behavior.
201
T &operator[] (const size_t index) { return data_[index]; }
202
const T &operator[] (const size_t index) const { return data_[index]; }
203
204
// These two are invalid if empty().
205
const T &back() const { return (*this)[size() - 1]; }
206
const T &front() const { return (*this)[0]; }
207
208
bool operator == (const FixedVec<T, MaxSize> &other) const {
209
if (count_ != other.count_)
210
return false;
211
for (int i = 0; i < count_; i++) {
212
if (!(data_[i] == other.data_[i])) {
213
return false;
214
}
215
}
216
return true;
217
}
218
219
private:
220
int count_ = 0; // first in the struct just so it's more visible in the VS debugger.
221
T data_[MaxSize];
222
};
223
224