FCV.hpp 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  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. #include <cstring>
  20. #include <cstdlib>
  21. namespace ZeroTier {
  22. /**
  23. * FCV is a Fixed Capacity Vector
  24. *
  25. * Attempts to resize, push, or access this vector beyond its capacity will
  26. * silently fail. The [] operator is NOT bounds checked!
  27. *
  28. * This doesn't implement everything in std::vector, just what we need. It
  29. * also adds a few special things for use in ZT core code.
  30. *
  31. * Note that an FCV will be TriviallyCopyable IF and only if its contained
  32. * type is TriviallyCopyable. There's a const static checker for this.
  33. *
  34. * @tparam T Type to contain
  35. * @tparam C Maximum capacity of vector
  36. */
  37. template<typename T,unsigned int C>
  38. class FCV
  39. {
  40. public:
  41. typedef T * iterator;
  42. typedef const T * const_iterator;
  43. ZT_INLINE FCV() noexcept : _s(0) {} // NOLINT(cppcoreguidelines-pro-type-member-init,hicpp-member-init)
  44. ZT_INLINE FCV(const FCV &v) : _s(0) { *this = v; } // NOLINT(cppcoreguidelines-pro-type-member-init,hicpp-member-init)
  45. template<typename I>
  46. ZT_INLINE FCV(I i,I end) : // NOLINT(cppcoreguidelines-pro-type-member-init,hicpp-member-init)
  47. _s(0)
  48. {
  49. while (i != end) {
  50. push_back(*i);
  51. ++i;
  52. }
  53. }
  54. ZT_INLINE ~FCV() { this->clear(); }
  55. ZT_INLINE FCV &operator=(const FCV &v)
  56. {
  57. if (&v != this) {
  58. this->clear();
  59. const unsigned int s = v._s;
  60. _s = s;
  61. for (unsigned int i=0;i<s;++i)
  62. new(reinterpret_cast<T *>(_m) + i) T(*(reinterpret_cast<const T *>(v._m) + i));
  63. }
  64. return *this;
  65. }
  66. /**
  67. * Clear this vector, destroying all content objects
  68. */
  69. ZT_INLINE void clear()
  70. {
  71. const unsigned int s = _s;
  72. _s = 0;
  73. for(unsigned int i=0;i<s;++i)
  74. (reinterpret_cast<T *>(_m) + i)->~T();
  75. }
  76. /**
  77. * Clear without calling destructors (same as unsafeResize(0))
  78. */
  79. ZT_INLINE void unsafeClear() noexcept { _s = 0; }
  80. /**
  81. * This does a straight copy of one vector's data to another
  82. *
  83. * @tparam C2 Inferred capacity of other vector
  84. * @param v Other vector to copy to this one
  85. */
  86. template<unsigned int C2>
  87. ZT_INLINE void unsafeAssign(const FCV<T,C2> &v) noexcept
  88. {
  89. _s = ((C2 > C)&&(v._s > C)) ? C : v._s;
  90. Utils::copy(_m,v._m,_s * sizeof(T));
  91. }
  92. /**
  93. * Move contents from this vector to another and clear this vector
  94. *
  95. * @param v Target vector
  96. */
  97. ZT_INLINE void unsafeMoveTo(FCV &v) noexcept
  98. {
  99. Utils::copy(v._m,_m,(v._s = _s) * sizeof(T));
  100. _s = 0;
  101. }
  102. ZT_INLINE iterator begin() noexcept { return reinterpret_cast<T *>(_m); }
  103. ZT_INLINE const_iterator begin() const noexcept { return reinterpret_cast<const T *>(_m); }
  104. ZT_INLINE iterator end() noexcept { return reinterpret_cast<T *>(_m) + _s; }
  105. ZT_INLINE const_iterator end() const noexcept { return reinterpret_cast<const T *>(_m) + _s; }
  106. ZT_INLINE T &operator[](const unsigned int i) noexcept { return reinterpret_cast<T *>(_m)[i]; }
  107. ZT_INLINE const T &operator[](const unsigned int i) const noexcept { return reinterpret_cast<T *>(_m)[i]; }
  108. ZT_INLINE unsigned int size() const noexcept { return _s; }
  109. ZT_INLINE bool empty() const noexcept { return (_s == 0); }
  110. ZT_INLINE T *data() noexcept { return reinterpret_cast<T *>(_m); }
  111. ZT_INLINE const T *data() const noexcept { return reinterpret_cast<const T *>(_m); }
  112. static constexpr unsigned int capacity() noexcept { return C; }
  113. /**
  114. * Push a value onto the back of this vector
  115. *
  116. * If the vector is at capacity this silently fails.
  117. *
  118. * @param v Value to push
  119. */
  120. ZT_INLINE void push_back(const T &v)
  121. {
  122. if (_s < C)
  123. new (reinterpret_cast<T *>(_m) + _s++) T(v);
  124. }
  125. /**
  126. * Push new default value or return last in vector if full.
  127. *
  128. * @return Reference to new item
  129. */
  130. ZT_INLINE T &push()
  131. {
  132. if (_s < C) {
  133. return *(new(reinterpret_cast<T *>(_m) + _s++) T());
  134. } else {
  135. return *(reinterpret_cast<T *>(_m) + (C - 1));
  136. }
  137. }
  138. /**
  139. * Push new default value or replace and return last in vector if full.
  140. *
  141. * @return Reference to new item
  142. */
  143. ZT_INLINE T &push(const T &v)
  144. {
  145. if (_s < C) {
  146. return *(new(reinterpret_cast<T *>(_m) + _s++) T(v));
  147. } else {
  148. T &tmp = *(reinterpret_cast<T *>(_m) + (C - 1));
  149. tmp = v;
  150. return tmp;
  151. }
  152. }
  153. /**
  154. * Remove the last element if this vector is not empty
  155. */
  156. ZT_INLINE void pop_back()
  157. {
  158. if (_s != 0)
  159. (reinterpret_cast<T *>(_m) + --_s)->~T();
  160. }
  161. /**
  162. * Resize vector
  163. *
  164. * @param ns New size (clipped to C if larger than capacity)
  165. */
  166. ZT_INLINE void resize(unsigned int ns)
  167. {
  168. if (ns > C)
  169. ns = C;
  170. unsigned int s = _s;
  171. while (s < ns)
  172. new(reinterpret_cast<T *>(_m) + s++) T();
  173. while (s > ns)
  174. (reinterpret_cast<T *>(_m) + --s)->~T();
  175. _s = s;
  176. }
  177. /**
  178. * Resize without calling any constructors or destructors on T
  179. *
  180. * This must only be called if T is a primitive type or is TriviallyCopyable and
  181. * safe to initialize from undefined contents.
  182. *
  183. * @param ns New size (clipped to C if larger than capacity)
  184. */
  185. ZT_INLINE void unsafeResize(const unsigned int ns) noexcept { _s = (ns > C) ? C : ns; }
  186. /**
  187. * This is a bounds checked auto-resizing variant of the [] operator
  188. *
  189. * If 'i' is out of bounds vs the current size of the vector, the vector is
  190. * resized. If that size would exceed C (capacity), 'i' is clipped to C-1.
  191. *
  192. * @param i Index to obtain as a reference, resizing if needed
  193. * @return Reference to value at this index
  194. */
  195. ZT_INLINE T &at(unsigned int i)
  196. {
  197. if (i >= _s) {
  198. if (unlikely(i >= C))
  199. i = C - 1;
  200. do {
  201. new(reinterpret_cast<T *>(_m) + _s++) T();
  202. } while (i >= _s);
  203. }
  204. return *(reinterpret_cast<T *>(_m) + i);
  205. }
  206. /**
  207. * Assign this vector's contents from a range of pointers or iterators
  208. *
  209. * If the range is larger than C it is truncated at C.
  210. *
  211. * @tparam X Inferred type of interators or pointers
  212. * @param start Starting iterator
  213. * @param end Ending iterator (must be greater than start)
  214. */
  215. template<typename X>
  216. ZT_INLINE void assign(X start,const X &end)
  217. {
  218. const int l = std::min((int)std::distance(start,end),(int)C);
  219. if (l > 0) {
  220. this->resize((unsigned int)l);
  221. for(int i=0;i<l;++i)
  222. reinterpret_cast<T *>(_m)[i] = *(start++);
  223. } else {
  224. this->clear();
  225. }
  226. }
  227. ZT_INLINE bool operator==(const FCV &v) const noexcept
  228. {
  229. if (_s == v._s) {
  230. for(unsigned int i=0;i<_s;++i) {
  231. if (!(*(reinterpret_cast<const T *>(_m) + i) == *(reinterpret_cast<const T *>(v._m) + i)))
  232. return false;
  233. }
  234. return true;
  235. }
  236. return false;
  237. }
  238. ZT_INLINE bool operator!=(const FCV &v) const noexcept { return (!(*this == v)); }
  239. ZT_INLINE bool operator<(const FCV &v) const noexcept { return std::lexicographical_compare(begin(),end(),v.begin(),v.end()); }
  240. ZT_INLINE bool operator>(const FCV &v) const noexcept { return (v < *this); }
  241. ZT_INLINE bool operator<=(const FCV &v) const noexcept { return !(v < *this); }
  242. ZT_INLINE bool operator>=(const FCV &v) const noexcept { return !(*this < v); }
  243. private:
  244. unsigned int _s;
  245. uint8_t _m[sizeof(T) * C];
  246. };
  247. } // namespace ZeroTier
  248. #endif