/* * Copyright (C) 2011-2019 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. */ #pragma once #include #include #include #include // This implements a reference counted array for POD** values, which is optimized for: // - An empty array only uses one word. // - A copy of the array only uses one word (i.e. assignment means aliasing). // - The vector can't grow beyond 2^32-1 elements. // - In all other regards this has similar space usage to a Vector. // // ** This could be modified to support non-POD values quite easily. It just // hasn't been, so far, because there has been no need. Moreover, even now, // it's used for things that aren't quite POD according to the official // defintion, such as JSC::Instruction. namespace WTF { DECLARE_ALLOCATOR_WITH_HEAP_IDENTIFIER(RefCountedArray); template> class RefCountedArray { enum CommonCopyConstructorTag { CommonCopyConstructor }; public: using iterator = T*; using const_iterator = const T*; using reverse_iterator = std::reverse_iterator; using const_reverse_iterator = std::reverse_iterator; RefCountedArray() = default; RefCountedArray(const RefCountedArray& other) : RefCountedArray(CommonCopyConstructor, other) { } template RefCountedArray(const RefCountedArray& other) : RefCountedArray(CommonCopyConstructor, other) { } RefCountedArray(RefCountedArray&& other) : m_data(PtrTraits::exchange(other.m_data, nullptr)) { } explicit RefCountedArray(size_t size) { if (!size) { // NOTE: JSC's LowLevelInterpreter relies on this being nullptr when the size is zero. PtrTraits::exchange(m_data, nullptr); return; } m_data = allocateUninitializedData(size); VectorTypeOperations::initializeIfNonPOD(begin(), end()); } template RefCountedArray clone() const { RefCountedArray result(size()); const T* data = this->data(); T* resultData = result.data(); for (unsigned i = size(); i--;) resultData[i] = data[i]; return result; } template explicit RefCountedArray(const Vector& other) { if (other.isEmpty()) { PtrTraits::exchange(m_data, nullptr); return; } T* data = allocateUninitializedData(other.size()); m_data = data; VectorTypeOperations::uninitializedCopy(other.begin(), other.end(), data); } template explicit RefCountedArray(Vector&& other) { Vector vector(WTFMove(other)); if (vector.isEmpty()) { PtrTraits::exchange(m_data, nullptr); return; } T* data = allocateUninitializedData(vector.size()); m_data = data; for (unsigned index = 0; index < vector.size(); ++index) new (data + index) T(WTFMove(vector[index])); } template RefCountedArray& operator=(const RefCountedArray& other) { return assign(other); } RefCountedArray& operator=(const RefCountedArray& other) { return assign(other); } template RefCountedArray& operator=(const Vector& other) { T* oldData = data(); if (other.isEmpty()) PtrTraits::exchange(m_data, nullptr); else { T* data = allocateUninitializedData(other.size()); m_data = data; VectorTypeOperations::uninitializedCopy(other.begin(), other.end(), data); } if (!oldData) return *this; unsigned refCount = Header::fromPayload(oldData)->refCount - 1; if (refCount) { Header::fromPayload(oldData)->refCount = refCount; return *this; } VectorTypeOperations::destruct(oldData, oldData + Header::fromPayload(oldData)->length); RefCountedArrayMalloc::free(Header::fromPayload(oldData)); return *this; } template RefCountedArray& operator=(Vector&& other) { Vector vector(WTFMove(other)); T* oldData = data(); if (vector.isEmpty()) PtrTraits::exchange(m_data, nullptr); else { T* data = allocateUninitializedData(vector.size()); m_data = data; for (unsigned index = 0; index < vector.size(); ++index) new (data + index) T(WTFMove(vector[index])); } if (!oldData) return *this; unsigned refCount = Header::fromPayload(oldData)->refCount - 1; if (refCount) { Header::fromPayload(oldData)->refCount = refCount; return *this; } VectorTypeOperations::destruct(oldData, oldData + Header::fromPayload(oldData)->length); RefCountedArrayMalloc::free(Header::fromPayload(oldData)); return *this; } ~RefCountedArray() { if (!m_data) return; T* data = this->data(); unsigned refCount = Header::fromPayload(data)->refCount - 1; if (refCount) { Header::fromPayload(data)->refCount = refCount; return; } VectorTypeOperations::destruct(begin(), end()); RefCountedArrayMalloc::free(Header::fromPayload(data)); } unsigned refCount() const { if (!m_data) return 0; return Header::fromPayload(data())->refCount; } size_t size() const { if (!m_data) return 0; return Header::fromPayload(data())->length; } bool isEmpty() const { return size() == 0; } size_t byteSize() const { return size() * sizeof(T); } T* data() { return PtrTraits::unwrap(m_data); } iterator begin() { return data(); } iterator end() { if (!m_data) return 0; T* data = this->data(); return data + Header::fromPayload(data)->length; } const T* data() const { return const_cast(this)->data(); } const_iterator begin() const { return const_cast(this)->begin(); } const_iterator end() const { return const_cast(this)->end(); } reverse_iterator rbegin() { return reverse_iterator(end()); } reverse_iterator rend() { return reverse_iterator(begin()); } const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); } const_reverse_iterator rend() const { return const_reverse_iterator(begin()); } T& at(size_t i) { ASSERT_WITH_SECURITY_IMPLICATION(i < size()); return begin()[i]; } const T& at(size_t i) const { ASSERT_WITH_SECURITY_IMPLICATION(i < size()); return begin()[i]; } T& operator[](size_t i) { return at(i); } const T& operator[](size_t i) const { return at(i); } T& first() { return (*this)[0]; } const T& first() const { return (*this)[0]; } T& last() { return (*this)[size() - 1]; } const T& last() const { return (*this)[size() - 1]; } void fill(const T& val) { std::fill(begin(), end(), val); } void swap(RefCountedArray& other) { PtrTraits::swap(m_data, other.m_data); } template bool operator==(const RefCountedArray& other) const { T* data = const_cast(this->data()); T* otherData = const_cast(other.data()); if (data == otherData) return true; if (!data || !otherData) return false; unsigned length = Header::fromPayload(data)->length; if (length != Header::fromPayload(otherData)->length) return false; for (unsigned i = 0; i < length; ++i) { if (data[i] != otherData[i]) return false; } return true; } bool operator==(const RefCountedArray& other) const { return this->operator==(other); } private: static T* allocateUninitializedData(unsigned length) { T* data = (static_cast(RefCountedArrayMalloc::malloc(Header::size() + sizeof(T) * length)))->payload(); Header::fromPayload(data)->refCount = 1; Header::fromPayload(data)->length = length; ASSERT(Header::fromPayload(data)->length == length); return data; } template RefCountedArray& assign(const RefCountedArray& other) { T* oldData = data(); T* otherData = const_cast(other.data()); if (otherData) Header::fromPayload(otherData)->refCount++; m_data = otherData; if (!oldData) return *this; unsigned refCount = Header::fromPayload(oldData)->refCount - 1; if (refCount) { Header::fromPayload(oldData)->refCount = refCount; return *this; } VectorTypeOperations::destruct(oldData, oldData + Header::fromPayload(oldData)->length); RefCountedArrayMalloc::free(Header::fromPayload(oldData)); return *this; } struct Header { unsigned refCount; unsigned length; static constexpr size_t size() { return (sizeof(Header) + 7) & ~7; } static ptrdiff_t offsetOfLength() { return OBJECT_OFFSETOF(Header, length); } T* payload() { char* result = reinterpret_cast(this) + size(); ASSERT(!(bitwise_cast(result) & 7)); return reinterpret_cast_ptr(result); } static Header* fromPayload(T* payload) { return reinterpret_cast_ptr(reinterpret_cast(payload) - size()); } static const Header* fromPayload(const T* payload) { return fromPayload(const_cast(payload)); } }; template RefCountedArray(CommonCopyConstructorTag, const RefCountedArray& other) : m_data(const_cast(other.data())) { if (m_data) Header::fromPayload(data())->refCount++; } friend class JSC::LLIntOffsetsExtractor; typename PtrTraits::StorageType m_data { nullptr }; }; } // namespace WTF using WTF::RefCountedArray;