FCV.hpp 7.5 KB

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