FCV.hpp 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. /*
  2. * Copyright (c)2013-2020 ZeroTier, Inc.
  3. *
  4. * Use of this software is governed by the Business Source License included
  5. * in the LICENSE.TXT file in the project's root directory.
  6. *
  7. * Change Date: 2024-01-01
  8. *
  9. * On the date above, in accordance with the Business Source License, use
  10. * of this software will be governed by version 2.0 of the Apache License.
  11. */
  12. /****/
  13. #ifndef ZT_FCV_HPP
  14. #define ZT_FCV_HPP
  15. #include "Constants.hpp"
  16. #include <iterator>
  17. #include <algorithm>
  18. #include <memory>
  19. namespace ZeroTier {
  20. /**
  21. * FCV is a Fixed Capacity Vector
  22. *
  23. * This doesn't implement everything in std::vector, just what we need. It
  24. * also adds a few special things for use in ZT core code.
  25. *
  26. * @tparam T Type to contain
  27. * @tparam C Maximum capacity of vector
  28. */
  29. template<typename T,unsigned int C>
  30. class FCV
  31. {
  32. public:
  33. typedef T * iterator;
  34. typedef const T * const_iterator;
  35. ZT_INLINE FCV() noexcept : _s(0) {}
  36. ZT_INLINE FCV(const FCV &v) : _s(0) { *this = v; }
  37. template<typename I>
  38. ZT_INLINE FCV(I i,I end) :
  39. _s(0)
  40. {
  41. while (i != end) {
  42. push_back(*i);
  43. ++i;
  44. }
  45. }
  46. ZT_INLINE ~FCV() { this->clear(); }
  47. ZT_INLINE FCV &operator=(const FCV &v)
  48. {
  49. if (likely(&v != this)) {
  50. this->clear();
  51. const unsigned int s = v._s;
  52. _s = s;
  53. for (unsigned int i=0;i<s;++i)
  54. new(reinterpret_cast<T *>(_m) + i) T(*(reinterpret_cast<const T *>(v._m) + i));
  55. }
  56. return *this;
  57. }
  58. /**
  59. * Clear this vector, destroying all content objects
  60. */
  61. ZT_INLINE void clear()
  62. {
  63. const unsigned int s = _s;
  64. _s = 0;
  65. for(unsigned int i=0;i<s;++i)
  66. (reinterpret_cast<T *>(_m) + i)->~T();
  67. }
  68. /**
  69. * Move contents from this vector to another and clear this vector.
  70. *
  71. * @param v Target vector
  72. */
  73. ZT_INLINE void unsafeMoveTo(FCV &v) noexcept
  74. {
  75. Utils::copy(v._m,_m,(v._s = _s) * sizeof(T));
  76. _s = 0;
  77. }
  78. ZT_INLINE iterator begin() noexcept { return reinterpret_cast<T *>(_m); }
  79. ZT_INLINE iterator end() noexcept { return reinterpret_cast<T *>(_m) + _s; }
  80. ZT_INLINE const_iterator begin() const noexcept { return reinterpret_cast<const T *>(_m); }
  81. ZT_INLINE const_iterator end() const noexcept { return reinterpret_cast<const T *>(_m) + _s; }
  82. ZT_INLINE T &operator[](const unsigned int i)
  83. {
  84. if (likely(i < _s))
  85. return reinterpret_cast<T *>(_m)[i];
  86. throw std::out_of_range("i > capacity");
  87. }
  88. ZT_INLINE const T &operator[](const unsigned int i) const
  89. {
  90. if (likely(i < _s))
  91. return reinterpret_cast<const T *>(_m)[i];
  92. throw std::out_of_range("i > capacity");
  93. }
  94. static constexpr unsigned int capacity() noexcept { return C; }
  95. ZT_INLINE unsigned int size() const noexcept { return _s; }
  96. ZT_INLINE bool empty() const noexcept { return (_s == 0); }
  97. ZT_INLINE T *data() noexcept { return reinterpret_cast<T *>(_m); }
  98. ZT_INLINE const T *data() const noexcept { return reinterpret_cast<const T *>(_m); }
  99. /**
  100. * Push a value onto the back of this vector
  101. *
  102. * If the vector is at capacity this silently fails.
  103. *
  104. * @param v Value to push
  105. */
  106. ZT_INLINE void push_back(const T &v)
  107. {
  108. if (likely(_s < C))
  109. new (reinterpret_cast<T *>(_m) + _s++) T(v);
  110. else throw std::out_of_range("capacity exceeded");
  111. }
  112. /**
  113. * Push new default value or return last in vector if full.
  114. *
  115. * @return Reference to new item
  116. */
  117. ZT_INLINE T &push()
  118. {
  119. if (likely(_s < C)) {
  120. return *(new(reinterpret_cast<T *>(_m) + _s++) T());
  121. } else {
  122. return *(reinterpret_cast<T *>(_m) + (C - 1));
  123. }
  124. }
  125. /**
  126. * Push new default value or replace and return last in vector if full.
  127. *
  128. * @return Reference to new item
  129. */
  130. ZT_INLINE T &push(const T &v)
  131. {
  132. if (likely(_s < C)) {
  133. return *(new(reinterpret_cast<T *>(_m) + _s++) T(v));
  134. } else {
  135. T &tmp = *(reinterpret_cast<T *>(_m) + (C - 1));
  136. tmp = v;
  137. return tmp;
  138. }
  139. }
  140. /**
  141. * Remove the last element if this vector is not empty
  142. */
  143. ZT_INLINE void pop_back()
  144. {
  145. if (likely(_s != 0))
  146. (reinterpret_cast<T *>(_m) + --_s)->~T();
  147. }
  148. /**
  149. * Resize vector
  150. *
  151. * @param ns New size (clipped to C if larger than capacity)
  152. */
  153. ZT_INLINE void resize(unsigned int ns)
  154. {
  155. if (unlikely(ns > C))
  156. throw std::out_of_range("capacity exceeded");
  157. unsigned int s = _s;
  158. while (s < ns)
  159. new(reinterpret_cast<T *>(_m) + s++) T();
  160. while (s > ns)
  161. (reinterpret_cast<T *>(_m) + --s)->~T();
  162. _s = s;
  163. }
  164. /**
  165. * This is a bounds checked auto-resizing variant of the [] operator
  166. *
  167. * If 'i' is out of bounds vs the current size of the vector, the vector is
  168. * resized. If that size would exceed C (capacity), 'i' is clipped to C-1.
  169. *
  170. * @param i Index to obtain as a reference, resizing if needed
  171. * @return Reference to value at this index
  172. */
  173. ZT_INLINE T &at(unsigned int i)
  174. {
  175. if (i >= _s) {
  176. if (unlikely(i >= C))
  177. i = C - 1;
  178. do {
  179. new(reinterpret_cast<T *>(_m) + _s++) T();
  180. } while (i >= _s);
  181. }
  182. return *(reinterpret_cast<T *>(_m) + i);
  183. }
  184. /**
  185. * Assign this vector's contents from a range of pointers or iterators
  186. *
  187. * If the range is larger than C it is truncated at C.
  188. *
  189. * @tparam X Inferred type of interators or pointers
  190. * @param start Starting iterator
  191. * @param end Ending iterator (must be greater than start)
  192. */
  193. template<typename X>
  194. ZT_INLINE void assign(X start,const X &end)
  195. {
  196. const int l = std::min((int)std::distance(start,end),(int)C);
  197. if (l > 0) {
  198. this->resize((unsigned int)l);
  199. for(int i=0;i<l;++i)
  200. reinterpret_cast<T *>(_m)[i] = *(start++);
  201. } else {
  202. this->clear();
  203. }
  204. }
  205. ZT_INLINE bool operator==(const FCV &v) const noexcept
  206. {
  207. if (_s == v._s) {
  208. for(unsigned int i=0;i<_s;++i) {
  209. if (!(*(reinterpret_cast<const T *>(_m) + i) == *(reinterpret_cast<const T *>(v._m) + i)))
  210. return false;
  211. }
  212. return true;
  213. }
  214. return false;
  215. }
  216. ZT_INLINE bool operator!=(const FCV &v) const noexcept { return (!(*this == v)); }
  217. ZT_INLINE bool operator<(const FCV &v) const noexcept { return std::lexicographical_compare(begin(),end(),v.begin(),v.end()); }
  218. ZT_INLINE bool operator>(const FCV &v) const noexcept { return (v < *this); }
  219. ZT_INLINE bool operator<=(const FCV &v) const noexcept { return !(v < *this); }
  220. ZT_INLINE bool operator>=(const FCV &v) const noexcept { return !(*this < v); }
  221. private:
  222. #ifdef _MSC_VER
  223. uint8_t _m[sizeof(T) * C];
  224. #else
  225. __attribute__((aligned(16))) uint8_t _m[sizeof(T) * C];
  226. #endif
  227. unsigned int _s;
  228. };
  229. } // namespace ZeroTier
  230. #endif