Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/manifold/src/vec.h
9905 views
1
// Copyright 2021 The Manifold Authors.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
// http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
15
#pragma once
16
#if TRACY_ENABLE && TRACY_MEMORY_USAGE
17
#include "tracy/Tracy.hpp"
18
#else
19
#define TracyAllocS(ptr, size, n) (void)0
20
#define TracyFreeS(ptr, n) (void)0
21
#endif
22
#include <vector>
23
24
#include "manifold/vec_view.h"
25
#include "parallel.h"
26
27
namespace manifold {
28
29
#if (MANIFOLD_PAR == 1)
30
extern tbb::task_arena gc_arena;
31
#endif
32
33
template <typename T>
34
class Vec;
35
36
/*
37
* Specialized vector implementation with multithreaded fill and uninitialized
38
* memory optimizations.
39
* Note that the constructor and resize function will not perform initialization
40
* if the parameter val is not set. Also, this implementation is a toy
41
* implementation that did not consider things like non-trivial
42
* constructor/destructor, please keep T trivial.
43
*/
44
template <typename T>
45
class Vec : public VecView<T> {
46
public:
47
Vec() {}
48
49
// Note that the vector constructed with this constructor will contain
50
// uninitialized memory. Please specify `val` if you need to make sure that
51
// the data is initialized.
52
Vec(size_t size) : VecView<T>() {
53
reserve(size);
54
this->size_ = size;
55
}
56
57
Vec(size_t size, T val) : VecView<T>() { resize(size, val); }
58
59
Vec(const Vec<T>& vec) : VecView<T>() { *this = Vec(vec.view()); }
60
61
Vec(const VecView<const T>& vec) : VecView<T>() {
62
this->size_ = vec.size();
63
this->capacity_ = this->size_;
64
auto policy = autoPolicy(this->size_);
65
if (this->size_ != 0) {
66
this->ptr_ = reinterpret_cast<T*>(malloc(this->size_ * sizeof(T)));
67
ASSERT(this->ptr_ != nullptr, std::bad_alloc());
68
TracyAllocS(this->ptr_, this->size_ * sizeof(T), 3);
69
copy(policy, vec.begin(), vec.end(), this->ptr_);
70
}
71
}
72
73
Vec(const std::vector<T>& vec) : VecView<T>() {
74
this->size_ = vec.size();
75
this->capacity_ = this->size_;
76
auto policy = autoPolicy(this->size_);
77
if (this->size_ != 0) {
78
this->ptr_ = reinterpret_cast<T*>(malloc(this->size_ * sizeof(T)));
79
ASSERT(this->ptr_ != nullptr, std::bad_alloc());
80
TracyAllocS(this->ptr_, this->size_ * sizeof(T), 3);
81
copy(policy, vec.begin(), vec.end(), this->ptr_);
82
}
83
}
84
85
Vec(Vec<T>&& vec) : VecView<T>() {
86
this->ptr_ = vec.ptr_;
87
this->size_ = vec.size_;
88
capacity_ = vec.capacity_;
89
vec.ptr_ = nullptr;
90
vec.size_ = 0;
91
vec.capacity_ = 0;
92
}
93
94
operator VecView<T>() { return {this->ptr_, this->size_}; }
95
operator VecView<const T>() const { return {this->ptr_, this->size_}; }
96
97
~Vec() {
98
if (this->ptr_ != nullptr) {
99
free_async(this->ptr_, capacity_);
100
}
101
this->ptr_ = nullptr;
102
this->size_ = 0;
103
capacity_ = 0;
104
}
105
106
Vec<T>& operator=(const Vec<T>& other) {
107
if (&other == this) return *this;
108
if (this->ptr_ != nullptr) {
109
free_async(this->ptr_, capacity_);
110
}
111
this->size_ = other.size_;
112
capacity_ = other.size_;
113
if (this->size_ != 0) {
114
this->ptr_ = reinterpret_cast<T*>(malloc(this->size_ * sizeof(T)));
115
ASSERT(this->ptr_ != nullptr, std::bad_alloc());
116
TracyAllocS(this->ptr_, this->size_ * sizeof(T), 3);
117
manifold::copy(other.begin(), other.end(), this->ptr_);
118
}
119
return *this;
120
}
121
122
Vec<T>& operator=(Vec<T>&& other) {
123
if (&other == this) return *this;
124
if (this->ptr_ != nullptr) {
125
free_async(this->ptr_, capacity_);
126
}
127
this->size_ = other.size_;
128
capacity_ = other.capacity_;
129
this->ptr_ = other.ptr_;
130
other.ptr_ = nullptr;
131
other.size_ = 0;
132
other.capacity_ = 0;
133
return *this;
134
}
135
136
operator VecView<T>() const { return {this->ptr_, this->size_}; }
137
138
void swap(Vec<T>& other) {
139
std::swap(this->ptr_, other.ptr_);
140
std::swap(this->size_, other.size_);
141
std::swap(capacity_, other.capacity_);
142
}
143
144
inline void push_back(const T& val) {
145
if (this->size_ >= capacity_) {
146
// avoid dangling pointer in case val is a reference of our array
147
T val_copy = val;
148
reserve(capacity_ == 0 ? 128 : capacity_ * 2);
149
this->ptr_[this->size_++] = val_copy;
150
return;
151
}
152
this->ptr_[this->size_++] = val;
153
}
154
155
inline void extend(size_t n) {
156
if (this->size_ + n >= capacity_)
157
reserve(capacity_ == 0 ? 128 : std::max(capacity_ * 2, this->size_ + n));
158
this->size_ += n;
159
}
160
161
void reserve(size_t n) {
162
if (n > capacity_) {
163
T* newBuffer = reinterpret_cast<T*>(malloc(n * sizeof(T)));
164
ASSERT(newBuffer != nullptr, std::bad_alloc());
165
TracyAllocS(newBuffer, n * sizeof(T), 3);
166
if (this->size_ > 0)
167
manifold::copy(autoPolicy(this->size_), this->ptr_,
168
this->ptr_ + this->size_, newBuffer);
169
if (this->ptr_ != nullptr) {
170
free_async(this->ptr_, capacity_);
171
}
172
this->ptr_ = newBuffer;
173
capacity_ = n;
174
}
175
}
176
177
void resize(size_t newSize, T val = T()) {
178
bool shrink = this->size_ > 2 * newSize && this->size_ > 16;
179
reserve(newSize);
180
if (this->size_ < newSize) {
181
fill(autoPolicy(newSize - this->size_), this->ptr_ + this->size_,
182
this->ptr_ + newSize, val);
183
}
184
this->size_ = newSize;
185
if (shrink) shrink_to_fit();
186
}
187
188
void resize_nofill(size_t newSize) {
189
bool shrink = this->size_ > 2 * newSize && this->size_ > 16;
190
reserve(newSize);
191
this->size_ = newSize;
192
if (shrink) shrink_to_fit();
193
}
194
195
void pop_back() { resize_nofill(this->size_ - 1); }
196
197
void clear(bool shrink = true) {
198
this->size_ = 0;
199
if (shrink) shrink_to_fit();
200
}
201
202
void shrink_to_fit() {
203
T* newBuffer = nullptr;
204
if (this->size_ > 0) {
205
newBuffer = reinterpret_cast<T*>(malloc(this->size_ * sizeof(T)));
206
ASSERT(newBuffer != nullptr, std::bad_alloc());
207
TracyAllocS(newBuffer, this->size_ * sizeof(T), 3);
208
manifold::copy(this->ptr_, this->ptr_ + this->size_, newBuffer);
209
}
210
if (this->ptr_ != nullptr) {
211
free_async(this->ptr_, capacity_);
212
}
213
this->ptr_ = newBuffer;
214
capacity_ = this->size_;
215
}
216
217
size_t capacity() const { return capacity_; }
218
219
private:
220
size_t capacity_ = 0;
221
222
static_assert(std::is_trivially_destructible<T>::value);
223
224
static void free_async(T* ptr, size_t size) {
225
// Only do async free if the size is large, because otherwise we may be able
226
// to reuse the allocation, and the deallocation probably won't trigger
227
// munmap.
228
// Currently it is set to 64 pages (4kB page).
229
constexpr size_t ASYNC_FREE_THRESHOLD = 1 << 18;
230
TracyFreeS(ptr, 3);
231
#if (MANIFOLD_PAR == 1)
232
if (size * sizeof(T) > ASYNC_FREE_THRESHOLD)
233
gc_arena.enqueue([ptr]() { free(ptr); });
234
else
235
#endif
236
free(ptr);
237
}
238
};
239
} // namespace manifold
240
241