Line data Source code
1 :
2 : /* Copyright (c) 2015-2016, Human Brain Project
3 : * Stefan.Eilemann@epfl.ch
4 : */
5 :
6 : #ifndef ZEROBUF_VECTOR_H
7 : #define ZEROBUF_VECTOR_H
8 :
9 : #include <zerobuf/DynamicSubAllocator.h> // used inline
10 : #include <zerobuf/Zerobuf.h> // sfinae type
11 : #include <zerobuf/json.h> // used inline
12 :
13 : #include <cstring> // memcmp
14 : #include <stdexcept> // std::runtime_error
15 : #include <typeinfo> // typeid
16 :
17 : namespace zerobuf
18 : {
19 : /**
20 : * STL-like vector abstraction for dynamic arrays in a zerobuf.
21 : *
22 : * @param T element type
23 : */
24 : template <class T>
25 : class Vector
26 : {
27 : public:
28 : /** Forward iterator for Vector< T > */
29 : template <class U>
30 : class Iterator : public std::iterator<std::forward_iterator_tag, U>
31 : {
32 : public:
33 4 : Iterator(U& vector, const size_t index)
34 : : _vector(vector)
35 4 : , _index(index)
36 : {
37 4 : }
38 :
39 : bool operator==(const Iterator& rhs) const
40 : {
41 : return _index == rhs._index;
42 : }
43 7 : bool operator!=(const Iterator& rhs) const
44 : {
45 7 : return _index != rhs._index;
46 : }
47 :
48 5 : Iterator& operator++()
49 : {
50 5 : ++_index;
51 5 : return *this;
52 : }
53 :
54 2 : const typename U::element_type& operator*() const
55 : {
56 2 : return _vector[_index];
57 : }
58 :
59 : template <class Q = U, typename = typename std::enable_if<
60 : !std::is_const<Q>::value>::type>
61 3 : typename U::element_type& operator*()
62 : {
63 3 : return _vector[_index];
64 : }
65 :
66 : private:
67 : U& _vector;
68 : size_t _index;
69 : };
70 :
71 : using element_type = T;
72 : using iterator = Iterator<Vector<T>>;
73 : using const_iterator = Iterator<const Vector<T>>;
74 :
75 : /** @internal
76 : * @param alloc The parent allocator that contains the data.
77 : * @param index Index of the vector in the parent allocator dynamic storage
78 : */
79 : Vector(Allocator& alloc, size_t index);
80 437 : ~Vector() {}
81 : /** @return true if the vector contains no elements. */
82 11 : bool empty() const { return _getSize() == 0; }
83 : /** @return the number of elements in the vector. */
84 537 : uint64_t size() const { return _getSize() / _getElementSize<T>(); }
85 : /** Empty the vector. */
86 : void clear();
87 :
88 : /** @return The pointer to the current allocation of the vector */
89 7 : T* data() { return _alloc->template getDynamic<T>(_index); }
90 : /** @return The pointer to the current allocation of the vector */
91 344 : const T* data() const
92 : {
93 344 : return const_cast<const Allocator*>(_alloc)->template getDynamic<T>(
94 688 : _index);
95 : }
96 :
97 : /** Resize the vector to new size; keeps elements untouched. */
98 : void resize(size_t size);
99 :
100 : /** @return true if the two vectors of builtins are identical. */
101 : bool operator==(const Vector& rhs) const;
102 : /** @return false if the two vectors are identical. */
103 : bool operator!=(const Vector& rhs) const;
104 :
105 : /** @return an immutable iterator to the first element. */
106 : const_iterator begin() const;
107 :
108 : /** @return an immutable iterator to the past-the-end element. */
109 : const_iterator end() const;
110 :
111 : /** @return a mutable iterator to the first element. */
112 : iterator begin();
113 :
114 : /** @return a mutable iterator to the past-the-end element. */
115 : iterator end();
116 :
117 : /** @return a builtin const element */
118 : template <class Q = T>
119 : const typename std::enable_if<!std::is_base_of<Zerobuf, Q>::value, Q>::type&
120 : operator[](const size_t index) const;
121 :
122 : /** @return a builtin element */
123 : template <class Q = T>
124 : typename std::enable_if<!std::is_base_of<Zerobuf, Q>::value, Q>::type&
125 : operator[](const size_t index);
126 :
127 : /** @return a Zerobuf-derived const element */
128 : template <class Q = T>
129 : const typename std::enable_if<std::is_base_of<Zerobuf, Q>::value, Q>::type&
130 : operator[](const size_t index) const;
131 :
132 : /** @return a Zerobuf-derived element */
133 : template <class Q = T>
134 : typename std::enable_if<std::is_base_of<Zerobuf, Q>::value, Q>::type&
135 : operator[](const size_t index);
136 :
137 : /** Insert a builtin element at the end of the vector. */
138 : template <class Q = T>
139 : void push_back(const typename std::enable_if<
140 : !std::is_base_of<Zerobuf, Q>::value, Q>::type&);
141 :
142 : /** Insert a Zerobuf-derived element at the end of the vector. */
143 : template <class Q = T>
144 : void push_back(const typename std::enable_if<
145 : std::is_base_of<Zerobuf, Q>::value, Q>::type&);
146 :
147 : /** @internal */
148 6 : void reset(Allocator& alloc)
149 : {
150 6 : _alloc = &alloc;
151 6 : _zerobufs.clear();
152 6 : }
153 :
154 : /** Remove unused memory from vector and all members. */
155 162 : void compact(float) { /* NOP: elements are static and clear frees */}
156 :
157 : /** Update this vector of ZeroBufs from its JSON representation. */
158 : template <class Q = T>
159 : void fromJSON(const Json::Value& json,
160 : const typename std::enable_if<
161 : std::is_base_of<Zerobuf, Q>::value, Q>::type* = nullptr);
162 :
163 : /** Update this vector of enums from its JSON representation. */
164 : template <class Q = T>
165 : void fromJSON(const Json::Value& json,
166 : const typename std::enable_if<std::is_enum<Q>::value,
167 : Q>::type* = nullptr);
168 :
169 : /** Update this vector of arithmetics from its JSON representation. */
170 : template <class Q = T>
171 : void fromJSON(const Json::Value& json,
172 : const typename std::enable_if<std::is_arithmetic<Q>::value,
173 : Q>::type* = nullptr);
174 :
175 : /** Update this vector of uint128_t from its JSON representation. */
176 : template <class Q = T>
177 : void fromJSON(
178 : const Json::Value& json,
179 : const typename std::enable_if<std::is_same<Q, servus::uint128_t>::value,
180 : Q>::type* = nullptr);
181 :
182 : /** @return the JSON representation of this vector of ZeroBufs. */
183 : template <class Q = T>
184 : void toJSON(
185 : Json::Value& json,
186 : const typename std::enable_if<std::is_base_of<Zerobuf, Q>::value,
187 : Q>::type* = nullptr) const;
188 :
189 : /** @return the JSON representation of this vector of enums. */
190 : template <class Q = T>
191 : void toJSON(Json::Value& json,
192 : const typename std::enable_if<std::is_enum<Q>::value,
193 : Q>::type* = nullptr) const;
194 :
195 : /** @return the JSON representation of this vector of arithmetics. */
196 : template <class Q = T>
197 : void toJSON(Json::Value& json,
198 : const typename std::enable_if<std::is_arithmetic<Q>::value,
199 : Q>::type* = nullptr) const;
200 :
201 : /** @return the JSON representation of this vector of uint128_t. */
202 : template <class Q = T>
203 : void toJSON(
204 : Json::Value& json,
205 : const typename std::enable_if<std::is_same<Q, servus::uint128_t>::value,
206 : Q>::type* = nullptr) const;
207 :
208 : /** Update this vector from its JSON, base64-encoded representation. */
209 : template <class Q = T>
210 : void fromJSONBinary(const Json::Value& json,
211 : const typename std::enable_if<std::is_pod<Q>::value,
212 : Q>::type* = nullptr);
213 :
214 : /** @return the JSON representation of this vector, with base64 encoding. */
215 : template <class Q = T>
216 : void toJSONBinary(Json::Value& json,
217 : const typename std::enable_if<std::is_pod<Q>::value,
218 : Q>::type* = nullptr) const;
219 :
220 : private:
221 : Allocator* _alloc;
222 : const size_t _index;
223 : mutable std::vector<T> _zerobufs;
224 :
225 : Vector() = delete;
226 : Vector(const Vector& rhs) = delete;
227 : Vector(const Vector&& rhs) = delete;
228 :
229 654 : size_t _getSize() const { return _alloc->getDynamicSize(_index); }
230 : void copyBuffer(uint8_t* data, size_t size);
231 :
232 : template <class Q = T>
233 348 : size_t _getElementSize(
234 : typename std::enable_if<std::is_base_of<Zerobuf, Q>::value, Q>::type* =
235 : 0) const
236 : {
237 348 : return Q::ZEROBUF_STATIC_SIZE();
238 : }
239 :
240 : template <class Q = T>
241 300 : size_t _getElementSize(
242 : typename std::enable_if<!std::is_base_of<Zerobuf, Q>::value, Q>::type* =
243 : 0) const
244 : {
245 300 : return sizeof(Q);
246 : }
247 : };
248 :
249 : // Implementation
250 : template <class T>
251 437 : inline Vector<T>::Vector(Allocator& alloc, const size_t index)
252 : : _alloc(&alloc)
253 437 : , _index(index)
254 : {
255 437 : }
256 :
257 : template <class T>
258 49 : inline void Vector<T>::clear()
259 : {
260 49 : _alloc->updateAllocation(_index, false, 0);
261 49 : _zerobufs.clear();
262 49 : }
263 :
264 : template <class T>
265 2 : inline void Vector<T>::resize(const size_t size_)
266 : {
267 4 : _alloc->updateAllocation(_index, true /*copy*/,
268 2 : size_ * _getElementSize<T>());
269 2 : }
270 :
271 : template <class T>
272 1 : inline typename Vector<T>::const_iterator Vector<T>::begin() const
273 : {
274 1 : return const_iterator(*this, 0);
275 : }
276 :
277 : template <class T>
278 1 : inline typename Vector<T>::const_iterator Vector<T>::end() const
279 : {
280 1 : return const_iterator(*this, size());
281 : }
282 :
283 : template <class T>
284 1 : inline typename Vector<T>::iterator Vector<T>::begin()
285 : {
286 1 : return iterator(*this, 0);
287 : }
288 :
289 : template <class T>
290 1 : inline typename Vector<T>::iterator Vector<T>::end()
291 : {
292 1 : return iterator(*this, size());
293 : }
294 :
295 : template <class T>
296 4 : inline bool Vector<T>::operator==(const Vector& rhs) const
297 : {
298 4 : if (this == &rhs)
299 1 : return true;
300 3 : const size_t size_ = _getSize();
301 3 : if (size_ != rhs._getSize())
302 1 : return false;
303 2 : if (size_ == 0)
304 1 : return true;
305 :
306 : // memory compare is valid for Zerobufs as well since they are right now
307 : // static and therefore will have the same layout
308 1 : return ::memcmp(static_cast<const void*>(data()),
309 2 : static_cast<const void*>(rhs.data()), size_) == 0;
310 : }
311 :
312 : template <class T>
313 : inline bool Vector<T>::operator!=(const Vector& rhs) const
314 : {
315 : return !(operator==(rhs));
316 : }
317 :
318 : template <class T>
319 : template <class Q>
320 : inline const typename std::enable_if<!std::is_base_of<Zerobuf, Q>::value,
321 69 : Q>::type& Vector<T>::
322 : operator[](const size_t index) const
323 : {
324 69 : if (index >= size())
325 1 : throw std::runtime_error("Vector out of bounds read");
326 :
327 68 : return data()[index];
328 : }
329 :
330 : template <class T>
331 : template <class Q>
332 : inline typename std::enable_if<!std::is_base_of<Zerobuf, Q>::value, Q>::type&
333 7 : Vector<T>::operator[](const size_t index)
334 : {
335 7 : if (index >= size())
336 1 : throw std::runtime_error("Vector out of bounds read");
337 :
338 6 : return data()[index];
339 : }
340 :
341 : template <class T>
342 : template <class Q>
343 : inline const typename std::enable_if<std::is_base_of<Zerobuf, Q>::value,
344 100 : Q>::type& Vector<T>::
345 : operator[](const size_t index) const
346 : {
347 100 : if (index >= size())
348 2 : throw std::runtime_error("Vector out of bounds read");
349 :
350 234 : while (_zerobufs.size() < index + 1)
351 68 : _zerobufs.emplace_back(AllocatorPtr(
352 136 : new DynamicSubAllocator(*_alloc, _index, _zerobufs.size(),
353 68 : _getElementSize<T>())));
354 98 : return _zerobufs[index];
355 : }
356 :
357 : template <class T>
358 : template <class Q>
359 : inline typename std::enable_if<std::is_base_of<Zerobuf, Q>::value, Q>::type&
360 43 : Vector<T>::operator[](const size_t index)
361 : {
362 43 : if (index >= size())
363 2 : throw std::runtime_error("Vector out of bounds read");
364 :
365 89 : while (_zerobufs.size() < index + 1)
366 24 : _zerobufs.emplace_back(AllocatorPtr(
367 48 : new DynamicSubAllocator(*_alloc, _index, _zerobufs.size(),
368 24 : _getElementSize<T>())));
369 41 : return _zerobufs[index];
370 : }
371 :
372 : template <class T>
373 : template <class Q>
374 13 : inline void Vector<T>::push_back(
375 : const typename std::enable_if<!std::is_base_of<Zerobuf, Q>::value, Q>::type&
376 : value)
377 : {
378 13 : const size_t size_ = _getSize();
379 : T* newPtr = reinterpret_cast<T*>(
380 13 : _alloc->updateAllocation(_index, true /*copy*/, size_ + sizeof(T)));
381 13 : newPtr[size_ / _getElementSize<T>()] = value;
382 13 : }
383 :
384 : template <class T>
385 : template <class Q>
386 79 : inline void Vector<T>::push_back(
387 : const typename std::enable_if<std::is_base_of<Zerobuf, Q>::value, Q>::type&
388 : value)
389 : {
390 79 : const size_t size_ = _getSize();
391 158 : const zerobuf::Data& zerobuf = value.toBinary();
392 : uint8_t* newPtr =
393 79 : _alloc->updateAllocation(_index, true /*copy*/, size_ + zerobuf.size);
394 79 : ::memcpy(newPtr + size_, zerobuf.ptr.get(), zerobuf.size);
395 79 : }
396 :
397 : template <class T>
398 : template <class Q>
399 4 : inline void Vector<T>::fromJSON(
400 : const Json::Value& json,
401 : const typename std::enable_if<std::is_base_of<Zerobuf, Q>::value, Q>::type*)
402 : {
403 4 : const size_t size_ = getJSONSize(json);
404 4 : _alloc->updateAllocation(_index, false, size_ * _getElementSize<T>());
405 17 : for (size_t i = 0; i < size_; ++i)
406 13 : zerobuf::fromJSON(getJSONField(json, i), (*this)[i]);
407 4 : }
408 :
409 : template <class T>
410 : template <class Q>
411 2 : inline void Vector<T>::fromJSON(
412 : const Json::Value& json,
413 : const typename std::enable_if<std::is_enum<Q>::value, Q>::type*)
414 : {
415 2 : const size_t size_ = getJSONSize(json);
416 : T* array = reinterpret_cast<T*>(
417 2 : _alloc->updateAllocation(_index, false /*no copy*/, size_ * sizeof(T)));
418 :
419 6 : for (size_t i = 0; i < size_; ++i)
420 4 : array[i] = string_to_enum<T>(
421 : zerobuf::fromJSON<std::string>(getJSONField(json, i)));
422 2 : }
423 :
424 : template <class T>
425 : template <class Q>
426 31 : inline void Vector<T>::fromJSON(
427 : const Json::Value& json,
428 : const typename std::enable_if<std::is_arithmetic<Q>::value, Q>::type*)
429 : {
430 31 : const size_t size_ = getJSONSize(json);
431 : T* array = reinterpret_cast<T*>(
432 31 : _alloc->updateAllocation(_index, false /*no copy*/, size_ * sizeof(T)));
433 :
434 94 : for (size_t i = 0; i < size_; ++i)
435 63 : array[i] = zerobuf::fromJSON<T>(getJSONField(json, i));
436 31 : }
437 :
438 : template <class T>
439 : template <class Q>
440 2 : inline void Vector<T>::fromJSON(
441 : const Json::Value& json,
442 : const typename std::enable_if<std::is_same<Q, servus::uint128_t>::value,
443 : Q>::type*)
444 : {
445 2 : const size_t size_ = getJSONSize(json);
446 : T* array = reinterpret_cast<T*>(
447 2 : _alloc->updateAllocation(_index, false /*no copy*/, size_ * sizeof(T)));
448 :
449 6 : for (size_t i = 0; i < size_; ++i)
450 4 : array[i] = zerobuf::fromJSON<T>(getJSONField(json, i));
451 2 : }
452 :
453 : template <class T>
454 : template <class Q>
455 16 : inline void Vector<T>::toJSON(
456 : Json::Value& json,
457 : const typename std::enable_if<std::is_base_of<Zerobuf, Q>::value, Q>::type*)
458 : const
459 : {
460 16 : const size_t size_ = size();
461 16 : zerobuf::emptyJSONArray(
462 : json); // return [] instead of null if array is empty
463 38 : for (size_t i = 0; i < size_; ++i)
464 22 : zerobuf::toJSON(static_cast<const Zerobuf&>((*this)[i]),
465 : getJSONField(json, i));
466 16 : }
467 :
468 : template <class T>
469 : template <class Q>
470 4 : inline void Vector<T>::toJSON(
471 : Json::Value& json,
472 : const typename std::enable_if<std::is_enum<Q>::value, Q>::type*) const
473 : {
474 4 : const size_t size_ = size();
475 4 : zerobuf::emptyJSONArray(
476 : json); // return [] instead of null if array is empty
477 8 : for (size_t i = 0; i < size_; ++i)
478 4 : zerobuf::toJSON(enum_to_string<T>((*this)[i]), getJSONField(json, i));
479 4 : }
480 :
481 : template <class T>
482 : template <class Q>
483 60 : inline void Vector<T>::toJSON(
484 : Json::Value& json,
485 : const typename std::enable_if<std::is_arithmetic<Q>::value, Q>::type*) const
486 : {
487 60 : const size_t size_ = size();
488 60 : zerobuf::emptyJSONArray(
489 : json); // return [] instead of null if array is empty
490 120 : for (size_t i = 0; i < size_; ++i)
491 60 : zerobuf::toJSON((*this)[i], getJSONField(json, i));
492 60 : }
493 :
494 : template <class T>
495 : template <class Q>
496 4 : inline void Vector<T>::toJSON(
497 : Json::Value& json,
498 : const typename std::enable_if<std::is_same<Q, servus::uint128_t>::value,
499 : Q>::type*) const
500 : {
501 4 : const size_t size_ = size();
502 4 : zerobuf::emptyJSONArray(
503 : json); // return [] instead of null if array is empty
504 8 : for (size_t i = 0; i < size_; ++i)
505 4 : zerobuf::toJSON((*this)[i], getJSONField(json, i));
506 4 : }
507 :
508 : template <class T>
509 : template <class Q>
510 4 : inline void Vector<T>::fromJSONBinary(
511 : const Json::Value& json,
512 : const typename std::enable_if<std::is_pod<Q>::value, Q>::type*)
513 : {
514 8 : const std::string& decoded = zerobuf::fromJSONBinary(json);
515 4 : copyBuffer((uint8_t*)decoded.data(), decoded.length());
516 4 : }
517 :
518 : template <class T>
519 : template <class Q>
520 8 : inline void Vector<T>::toJSONBinary(
521 : Json::Value& json,
522 : const typename std::enable_if<std::is_pod<Q>::value, Q>::type*) const
523 : {
524 8 : zerobuf::toJSONBinary(data(), _getSize(), json);
525 8 : }
526 :
527 : template <class T>
528 4 : inline void Vector<T>::copyBuffer(uint8_t* data_, size_t size_)
529 : {
530 4 : void* to = _alloc->updateAllocation(_index, false /*no copy*/, size_);
531 4 : ::memcpy(to, data_, size_);
532 4 : }
533 :
534 : template <class T>
535 0 : inline std::ostream& operator<<(std::ostream& os, const Vector<T>& vector)
536 : {
537 0 : return os << typeid(vector).name() << " of size " << vector.size();
538 : }
539 :
540 : template <>
541 0 : inline std::ostream& operator<<(std::ostream& os, const Vector<char>& string)
542 : {
543 0 : return os << string.data();
544 : }
545 : }
546 :
547 : #endif
|