LCOV - code coverage report
Current view: top level - zerobuf - Vector.h (source / functions) Hit Total Coverage
Test: ZeroBuf Lines: 152 156 97.4 %
Date: 2018-01-09 16:38:26 Functions: 180 196 91.8 %

          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

Generated by: LCOV version 1.11