treelite
contiguous_array.h
Go to the documentation of this file.
1 
7 #ifndef TREELITE_DETAIL_CONTIGUOUS_ARRAY_H_
8 #define TREELITE_DETAIL_CONTIGUOUS_ARRAY_H_
9 
10 #include <treelite/logging.h>
11 
12 #include <algorithm>
13 #include <cstddef>
14 #include <cstring>
15 #include <vector>
16 
17 namespace treelite {
18 
19 template <typename T>
21  : buffer_(nullptr), size_(0), capacity_(0), owned_buffer_(true) {}
22 
23 template <typename T>
25  if (buffer_ && owned_buffer_) {
26  std::free(buffer_);
27  }
28 }
29 
30 template <typename T>
31 ContiguousArray<T>::ContiguousArray(std::vector<T> const& other) {
32  buffer_ = static_cast<T*>(std::malloc(sizeof(T) * other.capacity()));
33  TREELITE_CHECK(buffer_) << "Could not allocate buffer";
34  std::memcpy(buffer_, other.data(), sizeof(T) * other.size());
35  size_ = other.size();
36  capacity_ = other.capacity();
37  owned_buffer_ = true;
38 }
39 
40 template <typename T>
41 ContiguousArray<T>& ContiguousArray<T>::operator=(std::vector<T> const& other) {
42  if (buffer_ && owned_buffer_) {
43  std::free(buffer_);
44  }
45  buffer_ = static_cast<T*>(std::malloc(sizeof(T) * other.capacity()));
46  TREELITE_CHECK(buffer_) << "Could not allocate buffer";
47  std::memcpy(buffer_, other.data(), sizeof(T) * other.size());
48  size_ = other.size();
49  capacity_ = other.capacity();
50  owned_buffer_ = true;
51  return *this;
52 }
53 
54 template <typename T>
56  : buffer_(other.buffer_),
57  size_(other.size_),
58  capacity_(other.capacity_),
59  owned_buffer_(other.owned_buffer_) {
60  other.buffer_ = nullptr;
61  other.size_ = other.capacity_ = 0;
62 }
63 
64 template <typename T>
66  if (buffer_ && owned_buffer_) {
67  std::free(buffer_);
68  }
69  buffer_ = other.buffer_;
70  size_ = other.size_;
71  capacity_ = other.capacity_;
72  owned_buffer_ = other.owned_buffer_;
73  other.buffer_ = nullptr;
74  other.size_ = other.capacity_ = 0;
75  return *this;
76 }
77 
78 template <typename T>
80  ContiguousArray clone;
81  if (buffer_) {
82  clone.buffer_ = static_cast<T*>(std::malloc(sizeof(T) * capacity_));
83  TREELITE_CHECK(clone.buffer_) << "Could not allocate memory for the clone";
84  std::memcpy(clone.buffer_, buffer_, sizeof(T) * size_);
85  } else {
86  TREELITE_CHECK_EQ(size_, 0);
87  TREELITE_CHECK_EQ(capacity_, 0);
88  clone.buffer_ = nullptr;
89  }
90  clone.size_ = size_;
91  clone.capacity_ = capacity_;
92  clone.owned_buffer_ = true;
93  return clone;
94 }
95 
96 template <typename T>
97 inline void ContiguousArray<T>::UseForeignBuffer(void* prealloc_buf, std::size_t size) {
98  if (buffer_ && owned_buffer_) {
99  std::free(buffer_);
100  }
101  buffer_ = static_cast<T*>(prealloc_buf);
102  size_ = size;
103  capacity_ = size;
104  owned_buffer_ = false;
105 }
106 
107 template <typename T>
109  return buffer_;
110 }
111 
112 template <typename T>
113 inline T const* ContiguousArray<T>::Data() const {
114  return buffer_;
115 }
116 
117 template <typename T>
119  return &buffer_[Size()];
120 }
121 
122 template <typename T>
123 inline T const* ContiguousArray<T>::End() const {
124  return &buffer_[Size()];
125 }
126 
127 template <typename T>
129  return buffer_[Size() - 1];
130 }
131 
132 template <typename T>
133 inline T const& ContiguousArray<T>::Back() const {
134  return buffer_[Size() - 1];
135 }
136 
137 template <typename T>
138 inline std::size_t ContiguousArray<T>::Size() const {
139  return size_;
140 }
141 
142 template <typename T>
143 inline bool ContiguousArray<T>::Empty() const {
144  return (Size() == 0);
145 }
146 
147 template <typename T>
148 inline void ContiguousArray<T>::Reserve(std::size_t newsize) {
149  TREELITE_CHECK(owned_buffer_) << "Cannot resize when using a foreign buffer; clone first";
150  T* newbuf = static_cast<T*>(std::realloc(static_cast<void*>(buffer_), sizeof(T) * newsize));
151  TREELITE_CHECK(newbuf) << "Could not expand buffer";
152  buffer_ = newbuf;
153  capacity_ = newsize;
154 }
155 
156 template <typename T>
157 inline void ContiguousArray<T>::Resize(std::size_t newsize) {
158  Resize(newsize, T{});
159 }
160 
161 template <typename T>
162 inline void ContiguousArray<T>::Resize(std::size_t newsize, T t) {
163  TREELITE_CHECK(owned_buffer_) << "Cannot resize when using a foreign buffer; clone first";
164  std::size_t oldsize = Size();
165  if (newsize > capacity_) {
166  std::size_t newcapacity = capacity_;
167  if (newcapacity == 0) {
168  newcapacity = 1;
169  }
170  while (newcapacity < newsize) {
171  newcapacity *= 2;
172  }
173  Reserve(newcapacity);
174  }
175  for (std::size_t i = oldsize; i < newsize; ++i) {
176  buffer_[i] = t;
177  }
178  size_ = newsize;
179 }
180 
181 template <typename T>
183  TREELITE_CHECK(owned_buffer_) << "Cannot clear when using a foreign buffer; clone first";
184  Resize(0);
185 }
186 
187 template <typename T>
189  TREELITE_CHECK(owned_buffer_) << "Cannot add element when using a foreign buffer; clone first";
190  if (size_ == capacity_) {
191  if (capacity_ == 0) {
192  Reserve(1);
193  } else {
194  Reserve(capacity_ * 2);
195  }
196  }
197  buffer_[size_++] = t;
198 }
199 
200 template <typename T>
201 inline void ContiguousArray<T>::Extend(std::vector<T> const& other) {
202  TREELITE_CHECK(owned_buffer_) << "Cannot add elements when using a foreign buffer; clone first";
203  if (other.empty()) {
204  return; // Appending an empty vector is a no-op
205  }
206  std::size_t newsize = size_ + other.size();
207  if (newsize > capacity_) {
208  std::size_t newcapacity = capacity_;
209  if (newcapacity == 0) {
210  newcapacity = 1;
211  }
212  while (newcapacity <= newsize) {
213  newcapacity *= 2;
214  }
215  Reserve(newcapacity);
216  }
217  std::memcpy(&buffer_[size_], static_cast<void const*>(other.data()), sizeof(T) * other.size());
218  size_ = newsize;
219 }
220 
221 template <typename T>
222 inline void ContiguousArray<T>::Extend(ContiguousArray const& other) {
223  TREELITE_CHECK(owned_buffer_) << "Cannot add elements when using a foreign buffer; clone first";
224  if (other.Empty()) {
225  return; // Appending an empty vector is a no-op
226  }
227  std::size_t newsize = size_ + other.Size();
228  if (newsize > capacity_) {
229  std::size_t newcapacity = capacity_;
230  if (newcapacity == 0) {
231  newcapacity = 1;
232  }
233  while (newcapacity <= newsize) {
234  newcapacity *= 2;
235  }
236  Reserve(newcapacity);
237  }
238  std::memcpy(&buffer_[size_], static_cast<void const*>(other.Data()), sizeof(T) * other.Size());
239  size_ = newsize;
240 }
241 
242 template <typename T>
243 inline std::vector<T> ContiguousArray<T>::AsVector() const {
244  auto const size = Size();
245  std::vector<T> vec(size);
246  std::copy(buffer_, buffer_ + size, vec.begin());
247  return vec;
248 }
249 
250 template <typename T>
252  if (Size() != other.Size()) {
253  return false;
254  }
255  for (std::size_t i = 0; i < Size(); ++i) {
256  if (buffer_[i] != other.buffer_[i]) {
257  return false;
258  }
259  }
260  return true;
261 }
262 
263 template <typename T>
264 inline T& ContiguousArray<T>::operator[](std::size_t idx) {
265  return buffer_[idx];
266 }
267 
268 template <typename T>
269 inline T const& ContiguousArray<T>::operator[](std::size_t idx) const {
270  return buffer_[idx];
271 }
272 
273 template <typename T>
274 inline T& ContiguousArray<T>::at(std::size_t idx) {
275  TREELITE_CHECK_LT(idx, Size()) << "nid out of range";
276  return buffer_[idx];
277 }
278 
279 template <typename T>
280 inline T const& ContiguousArray<T>::at(std::size_t idx) const {
281  TREELITE_CHECK_LT(idx, Size()) << "nid out of range";
282  return buffer_[idx];
283 }
284 
285 template <typename T>
286 inline T& ContiguousArray<T>::at(int idx) {
287  if (idx < 0 || static_cast<std::size_t>(idx) >= Size()) {
288  TREELITE_LOG(FATAL) << "nid out of range";
289  }
290  return buffer_[static_cast<std::size_t>(idx)];
291 }
292 
293 template <typename T>
294 inline T const& ContiguousArray<T>::at(int idx) const {
295  if (idx < 0 || static_cast<std::size_t>(idx) >= Size()) {
296  TREELITE_LOG(FATAL) << "nid out of range";
297  }
298  return buffer_[static_cast<std::size_t>(idx)];
299 }
300 
301 } // namespace treelite
302 
303 #endif // TREELITE_DETAIL_CONTIGUOUS_ARRAY_H_
Definition: contiguous_array.h:17
T * End()
Definition: contiguous_array.h:118
T & Back()
Definition: contiguous_array.h:128
ContiguousArray Clone() const
Definition: contiguous_array.h:79
bool Empty() const
Definition: contiguous_array.h:143
~ContiguousArray()
Definition: contiguous_array.h:24
T & operator[](std::size_t idx)
Definition: contiguous_array.h:264
std::vector< T > AsVector() const
Definition: contiguous_array.h:243
void Reserve(std::size_t newsize)
Definition: contiguous_array.h:148
T * Data()
Definition: contiguous_array.h:108
std::size_t Size() const
Definition: contiguous_array.h:138
T & at(std::size_t idx)
Definition: contiguous_array.h:274
void PushBack(T t)
Definition: contiguous_array.h:188
void Resize(std::size_t newsize)
Definition: contiguous_array.h:157
ContiguousArray()
Definition: contiguous_array.h:20
void UseForeignBuffer(void *prealloc_buf, std::size_t size)
Definition: contiguous_array.h:97
ContiguousArray & operator=(ContiguousArray const &)=delete
void Clear()
Definition: contiguous_array.h:182
void Extend(std::vector< T > const &other)
Definition: contiguous_array.h:201
bool operator==(ContiguousArray const &other)
Definition: contiguous_array.h:251
logging facility for Treelite
#define TREELITE_LOG(severity)
Definition: logging.h:84
#define TREELITE_CHECK(x)
Definition: logging.h:70
#define TREELITE_CHECK_EQ(x, y)
Definition: logging.h:77
#define TREELITE_CHECK_LT(x, y)
Definition: logging.h:73
Definition: contiguous_array.h:14