summaryrefslogtreecommitdiff
path: root/Source/WTF/wtf/SmallPtrSet.h
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WTF/wtf/SmallPtrSet.h')
-rw-r--r--Source/WTF/wtf/SmallPtrSet.h253
1 files changed, 253 insertions, 0 deletions
diff --git a/Source/WTF/wtf/SmallPtrSet.h b/Source/WTF/wtf/SmallPtrSet.h
new file mode 100644
index 000000000..da36c8da4
--- /dev/null
+++ b/Source/WTF/wtf/SmallPtrSet.h
@@ -0,0 +1,253 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef SmallPtrSet_h
+#define SmallPtrSet_h
+
+#include <wtf/Assertions.h>
+#include <wtf/FastMalloc.h>
+#include <wtf/HashFunctions.h>
+#include <wtf/Noncopyable.h>
+
+namespace WTF {
+
+template<typename PtrType, unsigned SmallArraySize = 8>
+class SmallPtrSet {
+ WTF_MAKE_NONCOPYABLE(SmallPtrSet);
+ static_assert(std::is_trivially_destructible<PtrType>::value, "We currently don't support non-trivially destructible pointer types.");
+ static_assert(sizeof(PtrType) == sizeof(void*), "Only support pointer sized things.");
+ static_assert(!(SmallArraySize & (SmallArraySize - 1)), "Inline size must be a power of two.");
+
+public:
+ SmallPtrSet()
+ {
+ initialize();
+ }
+
+ // We take care to have SmallPtrSet have partial move semantics allowable through
+ // memcpy. It's partial move semantics because our destructor should not be called
+ // on the SmallPtrObject in the old memory we were moved from (otherwise, we might free m_buffer twice)
+ // unless that old memory is reset to be isSmall(). See move constructor below.
+ // To maintain these semantics, we determine if we're small by checking our size
+ // and not our m_buffer pointer. And when we're small, we don't do operations on
+ // m_buffer, instead, we perform operations on m_smallStorage directly. The reason we want
+ // these semantics is that it's beneficial to have a Vector that contains SmallPtrSet
+ // (or an object with SmallPtrSet as a field) be allowed to use memcpy for its move operation.
+
+ SmallPtrSet(SmallPtrSet&& other)
+ {
+ memcpy(this, &other, sizeof(SmallPtrSet));
+ other.initialize();
+ }
+
+ SmallPtrSet& operator=(SmallPtrSet&& other)
+ {
+ this->~SmallPtrSet();
+ new (this) SmallPtrSet(WTFMove(other));
+ return *this;
+ }
+
+ ~SmallPtrSet()
+ {
+ if (!isSmall())
+ fastFree(m_buffer);
+ }
+
+ inline void add(PtrType ptr)
+ {
+ ASSERT(isValidEntry(ptr));
+
+ if (isSmall()) {
+ for (unsigned i = 0; i < m_size; i++) {
+ if (m_smallStorage[i] == ptr)
+ return;
+ }
+
+ if (m_size < SmallArraySize) {
+ m_smallStorage[m_size] = ptr;
+ ++m_size;
+ return;
+ }
+
+ grow(std::max(64u, SmallArraySize * 2));
+ // Fall through. We're no longer small :(
+ }
+
+ // If we're more than 3/4ths full we grow.
+ if (UNLIKELY(m_size * 4 >= m_capacity * 3)) {
+ grow(m_capacity * 2);
+ ASSERT(!(m_capacity & (m_capacity - 1)));
+ }
+
+ void** bucket = this->bucket(ptr);
+ if (*bucket != ptr) {
+ *bucket = ptr;
+ ++m_size;
+ }
+ }
+
+ inline bool contains(PtrType ptr) const
+ {
+ ASSERT(isValidEntry(ptr));
+ if (isSmall()) {
+ for (unsigned i = 0; i < m_size; i++) { // We only need to search up to m_size because we store things linearly inside m_smallStorage.
+ if (m_smallStorage[i] == ptr)
+ return true;
+ }
+ return false;
+ }
+
+ void** bucket = this->bucket(ptr);
+ return *bucket == ptr;
+ }
+
+ class iterator {
+ public:
+ iterator& operator++()
+ {
+ m_index++;
+ ASSERT(m_index <= m_capacity);
+ while (m_index < m_capacity && m_buffer[m_index] == emptyValue())
+ m_index++;
+ return *this;
+ }
+
+ PtrType operator*() const { ASSERT(m_index < m_capacity); return static_cast<PtrType>(m_buffer[m_index]); }
+ bool operator==(const iterator& other) const { ASSERT(m_buffer == other.m_buffer); return m_index == other.m_index; }
+ bool operator!=(const iterator& other) const { ASSERT(m_buffer == other.m_buffer); return !(*this == other); }
+
+ private:
+ template<typename U, unsigned S> friend class WTF::SmallPtrSet;
+ unsigned m_index;
+ unsigned m_capacity;
+ void** m_buffer;
+ };
+
+ iterator begin() const
+ {
+ iterator it;
+ it.m_index = std::numeric_limits<unsigned>::max();
+ it.m_capacity = m_capacity;
+ if (isSmall())
+ it.m_buffer = const_cast<void**>(m_smallStorage);
+ else
+ it.m_buffer = m_buffer;
+
+ ++it;
+
+ return it;
+ }
+
+ iterator end() const
+ {
+ iterator it;
+ it.m_index = m_capacity;
+ it.m_capacity = m_capacity;
+ if (isSmall())
+ it.m_buffer = const_cast<void**>(m_smallStorage);
+ else
+ it.m_buffer = m_buffer;
+
+ return it;
+ }
+
+ inline unsigned size() const { return m_size; }
+
+private:
+ constexpr static void* emptyValue()
+ {
+ return bitwise_cast<void*>(std::numeric_limits<uintptr_t>::max());
+ }
+
+ bool isValidEntry(const PtrType ptr) const
+ {
+ return ptr != emptyValue();
+ }
+
+ inline bool isSmall() const
+ {
+ return m_capacity == SmallArraySize;
+ }
+
+ inline void initialize()
+ {
+ m_size = 0;
+ m_buffer = nullptr;
+ m_capacity = SmallArraySize;
+ memset(m_smallStorage, -1, sizeof(void*) * SmallArraySize);
+ ASSERT(isSmall());
+ }
+
+ inline void grow(unsigned size)
+ {
+ ASSERT(static_cast<int32_t>(bitwise_cast<intptr_t>(emptyValue())) == -1);
+
+ size_t allocationSize = sizeof(void*) * size;
+ bool wasSmall = isSmall();
+ void** oldBuffer = wasSmall ? m_smallStorage : m_buffer;
+ unsigned oldCapacity = m_capacity;
+ m_buffer = static_cast<void**>(fastMalloc(allocationSize));
+ memset(m_buffer, -1, allocationSize);
+ m_capacity = size;
+
+ for (unsigned i = 0; i < oldCapacity; i++) {
+ if (oldBuffer[i] != emptyValue()) {
+ void** ptr = this->bucket(static_cast<PtrType>(oldBuffer[i]));
+ *ptr = oldBuffer[i];
+ }
+ }
+
+ if (!wasSmall)
+ fastFree(oldBuffer);
+ }
+
+
+ inline void** bucket(PtrType target) const
+ {
+ ASSERT(!(m_capacity & (m_capacity - 1)));
+ unsigned bucket = PtrHashBase<PtrType, false /* isSmartPtr */>::hash(target) & (m_capacity - 1);
+ unsigned index = 0;
+ while (true) {
+ void** ptr = m_buffer + bucket;
+ if (*ptr == emptyValue())
+ return ptr;
+ if (*ptr == target)
+ return ptr;
+ index++;
+ bucket = (bucket + index) & (m_capacity - 1);
+ }
+ }
+
+ unsigned m_size;
+ unsigned m_capacity;
+ void** m_buffer;
+ void* m_smallStorage[SmallArraySize];
+};
+
+} // namespace WTF
+
+using WTF::SmallPtrSet;
+
+#endif // SmallPtrSet_h