SegmentedVector.h 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. // A simple segmented vector class.
  6. //
  7. // This class should be used in preference to mozilla::Vector or nsTArray when
  8. // you are simply gathering items in order to later iterate over them.
  9. //
  10. // - In the case where you don't know the final size in advance, using
  11. // SegmentedVector avoids the need to repeatedly allocate increasingly large
  12. // buffers and copy the data into them.
  13. //
  14. // - In the case where you know the final size in advance and so can set the
  15. // capacity appropriately, using SegmentedVector still avoids the need for
  16. // large allocations (which can trigger OOMs).
  17. #ifndef mozilla_SegmentedVector_h
  18. #define mozilla_SegmentedVector_h
  19. #include "mozilla/Alignment.h"
  20. #include "mozilla/AllocPolicy.h"
  21. #include "mozilla/Array.h"
  22. #include "mozilla/LinkedList.h"
  23. #include "mozilla/MemoryReporting.h"
  24. #include "mozilla/Move.h"
  25. #include "mozilla/TypeTraits.h"
  26. #include <new> // for placement new
  27. namespace mozilla {
  28. // |IdealSegmentSize| specifies how big each segment will be in bytes (or as
  29. // close as is possible). Use the following guidelines to choose a size.
  30. //
  31. // - It should be a power-of-two, to avoid slop.
  32. //
  33. // - It should not be too small, so that segment allocations are infrequent,
  34. // and so that per-segment bookkeeping overhead is low. Typically each
  35. // segment should be able to hold hundreds of elements, at least.
  36. //
  37. // - It should not be too large, so that OOMs are unlikely when allocating
  38. // segments, and so that not too much space is wasted when the final segment
  39. // is not full.
  40. //
  41. // The ideal size depends on how the SegmentedVector is used and the size of
  42. // |T|, but reasonable sizes include 1024, 4096 (the default), 8192, and 16384.
  43. //
  44. template<typename T,
  45. size_t IdealSegmentSize = 4096,
  46. typename AllocPolicy = MallocAllocPolicy>
  47. class SegmentedVector : private AllocPolicy
  48. {
  49. template<size_t SegmentCapacity>
  50. struct SegmentImpl
  51. : public mozilla::LinkedListElement<SegmentImpl<SegmentCapacity>>
  52. {
  53. SegmentImpl() : mLength(0) {}
  54. ~SegmentImpl()
  55. {
  56. for (uint32_t i = 0; i < mLength; i++) {
  57. (*this)[i].~T();
  58. }
  59. }
  60. uint32_t Length() const { return mLength; }
  61. T* Elems() { return reinterpret_cast<T*>(&mStorage.mBuf); }
  62. T& operator[](size_t aIndex)
  63. {
  64. MOZ_ASSERT(aIndex < mLength);
  65. return Elems()[aIndex];
  66. }
  67. const T& operator[](size_t aIndex) const
  68. {
  69. MOZ_ASSERT(aIndex < mLength);
  70. return Elems()[aIndex];
  71. }
  72. template<typename U>
  73. void Append(U&& aU)
  74. {
  75. MOZ_ASSERT(mLength < SegmentCapacity);
  76. // Pre-increment mLength so that the bounds-check in operator[] passes.
  77. mLength++;
  78. T* elem = &(*this)[mLength - 1];
  79. new (elem) T(mozilla::Forward<U>(aU));
  80. }
  81. void PopLast()
  82. {
  83. MOZ_ASSERT(mLength > 0);
  84. (*this)[mLength - 1].~T();
  85. mLength--;
  86. }
  87. uint32_t mLength;
  88. // The union ensures that the elements are appropriately aligned.
  89. union Storage
  90. {
  91. char mBuf[sizeof(T) * SegmentCapacity];
  92. mozilla::AlignedElem<MOZ_ALIGNOF(T)> mAlign;
  93. } mStorage;
  94. static_assert(MOZ_ALIGNOF(T) == MOZ_ALIGNOF(Storage),
  95. "SegmentedVector provides incorrect alignment");
  96. };
  97. // See how many we elements we can fit in a segment of IdealSegmentSize. If
  98. // IdealSegmentSize is too small, it'll be just one. The +1 is because
  99. // kSingleElementSegmentSize already accounts for one element.
  100. static const size_t kSingleElementSegmentSize = sizeof(SegmentImpl<1>);
  101. static const size_t kSegmentCapacity =
  102. kSingleElementSegmentSize <= IdealSegmentSize
  103. ? (IdealSegmentSize - kSingleElementSegmentSize) / sizeof(T) + 1
  104. : 1;
  105. public:
  106. typedef SegmentImpl<kSegmentCapacity> Segment;
  107. // The |aIdealSegmentSize| is only for sanity checking. If it's specified, we
  108. // check that the actual segment size is as close as possible to it. This
  109. // serves as a sanity check for SegmentedVectorCapacity's capacity
  110. // computation.
  111. explicit SegmentedVector(size_t aIdealSegmentSize = 0)
  112. {
  113. // The difference between the actual segment size and the ideal segment
  114. // size should be less than the size of a single element... unless the
  115. // ideal size was too small, in which case the capacity should be one.
  116. MOZ_ASSERT_IF(
  117. aIdealSegmentSize != 0,
  118. (sizeof(Segment) > aIdealSegmentSize && kSegmentCapacity == 1) ||
  119. aIdealSegmentSize - sizeof(Segment) < sizeof(T));
  120. }
  121. ~SegmentedVector() { Clear(); }
  122. bool IsEmpty() const { return !mSegments.getFirst(); }
  123. // Note that this is O(n) rather than O(1), but the constant factor is very
  124. // small because it only has to do one addition per segment.
  125. size_t Length() const
  126. {
  127. size_t n = 0;
  128. for (auto segment = mSegments.getFirst();
  129. segment;
  130. segment = segment->getNext()) {
  131. n += segment->Length();
  132. }
  133. return n;
  134. }
  135. // Returns false if the allocation failed. (If you are using an infallible
  136. // allocation policy, use InfallibleAppend() instead.)
  137. template<typename U>
  138. MOZ_MUST_USE bool Append(U&& aU)
  139. {
  140. Segment* last = mSegments.getLast();
  141. if (!last || last->Length() == kSegmentCapacity) {
  142. last = this->template pod_malloc<Segment>(1);
  143. if (!last) {
  144. return false;
  145. }
  146. new (last) Segment();
  147. mSegments.insertBack(last);
  148. }
  149. last->Append(mozilla::Forward<U>(aU));
  150. return true;
  151. }
  152. // You should probably only use this instead of Append() if you are using an
  153. // infallible allocation policy. It will crash if the allocation fails.
  154. template<typename U>
  155. void InfallibleAppend(U&& aU)
  156. {
  157. bool ok = Append(mozilla::Forward<U>(aU));
  158. MOZ_RELEASE_ASSERT(ok);
  159. }
  160. void Clear()
  161. {
  162. Segment* segment;
  163. while ((segment = mSegments.popFirst())) {
  164. segment->~Segment();
  165. this->free_(segment);
  166. }
  167. }
  168. T& GetLast()
  169. {
  170. MOZ_ASSERT(!IsEmpty());
  171. Segment* last = mSegments.getLast();
  172. return (*last)[last->Length() - 1];
  173. }
  174. const T& GetLast() const
  175. {
  176. MOZ_ASSERT(!IsEmpty());
  177. Segment* last = mSegments.getLast();
  178. return (*last)[last->Length() - 1];
  179. }
  180. void PopLast()
  181. {
  182. MOZ_ASSERT(!IsEmpty());
  183. Segment* last = mSegments.getLast();
  184. last->PopLast();
  185. if (!last->Length()) {
  186. mSegments.popLast();
  187. last->~Segment();
  188. this->free_(last);
  189. }
  190. }
  191. // Equivalent to calling |PopLast| |aNumElements| times, but potentially
  192. // more efficient.
  193. void PopLastN(uint32_t aNumElements)
  194. {
  195. MOZ_ASSERT(aNumElements <= Length());
  196. Segment* last;
  197. // Pop full segments for as long as we can. Note that this loop
  198. // cleanly handles the case when the initial last segment is not
  199. // full and we are popping more elements than said segment contains.
  200. do {
  201. last = mSegments.getLast();
  202. // The list is empty. We're all done.
  203. if (!last) {
  204. return;
  205. }
  206. // Check to see if the list contains too many elements. Handle
  207. // that in the epilogue.
  208. uint32_t segmentLen = last->Length();
  209. if (segmentLen > aNumElements) {
  210. break;
  211. }
  212. // Destroying the segment destroys all elements contained therein.
  213. mSegments.popLast();
  214. last->~Segment();
  215. this->free_(last);
  216. MOZ_ASSERT(aNumElements >= segmentLen);
  217. aNumElements -= segmentLen;
  218. if (aNumElements == 0) {
  219. return;
  220. }
  221. } while (true);
  222. // Handle the case where the last segment contains more elements
  223. // than we want to pop.
  224. MOZ_ASSERT(last);
  225. MOZ_ASSERT(last == mSegments.getLast());
  226. MOZ_ASSERT(aNumElements < last->Length());
  227. for (uint32_t i = 0; i < aNumElements; ++i) {
  228. last->PopLast();
  229. }
  230. MOZ_ASSERT(last->Length() != 0);
  231. }
  232. // Use this class to iterate over a SegmentedVector, like so:
  233. //
  234. // for (auto iter = v.Iter(); !iter.Done(); iter.Next()) {
  235. // MyElem& elem = iter.Get();
  236. // f(elem);
  237. // }
  238. //
  239. // Note, adding new entries to the SegmentedVector while using iterators
  240. // is supported, but removing is not!
  241. // If an iterator has entered Done() state, adding more entries to the
  242. // vector doesn't affect it.
  243. class IterImpl
  244. {
  245. friend class SegmentedVector;
  246. Segment* mSegment;
  247. size_t mIndex;
  248. explicit IterImpl(SegmentedVector* aVector, bool aFromFirst)
  249. : mSegment(aFromFirst ? aVector->mSegments.getFirst() :
  250. aVector->mSegments.getLast())
  251. , mIndex(aFromFirst ? 0 :
  252. (mSegment ? mSegment->Length() - 1 : 0))
  253. {
  254. MOZ_ASSERT_IF(mSegment, mSegment->Length() > 0);
  255. }
  256. public:
  257. bool Done() const { return !mSegment; }
  258. T& Get()
  259. {
  260. MOZ_ASSERT(!Done());
  261. return (*mSegment)[mIndex];
  262. }
  263. const T& Get() const
  264. {
  265. MOZ_ASSERT(!Done());
  266. return (*mSegment)[mIndex];
  267. }
  268. void Next()
  269. {
  270. MOZ_ASSERT(!Done());
  271. mIndex++;
  272. if (mIndex == mSegment->Length()) {
  273. mSegment = mSegment->getNext();
  274. mIndex = 0;
  275. }
  276. }
  277. void Prev()
  278. {
  279. MOZ_ASSERT(!Done());
  280. if (mIndex == 0) {
  281. mSegment = mSegment->getPrevious();
  282. if (mSegment) {
  283. mIndex = mSegment->Length() - 1;
  284. }
  285. } else {
  286. --mIndex;
  287. }
  288. }
  289. };
  290. IterImpl Iter() { return IterImpl(this, true); }
  291. IterImpl IterFromLast() { return IterImpl(this, false); }
  292. // Measure the memory consumption of the vector excluding |this|. Note that
  293. // it only measures the vector itself. If the vector elements contain
  294. // pointers to other memory blocks, those blocks must be measured separately
  295. // during a subsequent iteration over the vector.
  296. size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
  297. {
  298. return mSegments.sizeOfExcludingThis(aMallocSizeOf);
  299. }
  300. // Like sizeOfExcludingThis(), but measures |this| as well.
  301. size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
  302. {
  303. return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
  304. }
  305. private:
  306. mozilla::LinkedList<Segment> mSegments;
  307. };
  308. } // namespace mozilla
  309. #endif /* mozilla_SegmentedVector_h */