| /* |
| * Copyright (C) 2009-2017 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 "ArrayBuffer.h" |
| #include "TypedArrayType.h" |
| #include <algorithm> |
| #include <limits.h> |
| #include <wtf/RefCounted.h> |
| #include <wtf/RefPtr.h> |
| |
| namespace JSC { |
| |
| class JSArrayBufferView; |
| class JSGlobalObject; |
| class ExecState; |
| |
| class ArrayBufferView : public RefCounted<ArrayBufferView> { |
| public: |
| virtual TypedArrayType getType() const = 0; |
| |
| bool isNeutered() const |
| { |
| return !m_buffer || m_buffer->isNeutered(); |
| } |
| |
| RefPtr<ArrayBuffer> possiblySharedBuffer() const |
| { |
| if (isNeutered()) |
| return nullptr; |
| return m_buffer; |
| } |
| |
| RefPtr<ArrayBuffer> unsharedBuffer() const |
| { |
| RefPtr<ArrayBuffer> result = possiblySharedBuffer(); |
| RELEASE_ASSERT(!result->isShared()); |
| return result; |
| } |
| |
| bool isShared() const |
| { |
| if (isNeutered()) |
| return false; |
| return m_buffer->isShared(); |
| } |
| |
| void* baseAddress() const |
| { |
| if (isNeutered()) |
| return 0; |
| return m_baseAddress.getMayBeNull(); |
| } |
| |
| void* data() const { return baseAddress(); } |
| |
| unsigned byteOffset() const |
| { |
| if (isNeutered()) |
| return 0; |
| return m_byteOffset; |
| } |
| |
| virtual unsigned byteLength() const = 0; |
| |
| JS_EXPORT_PRIVATE void setNeuterable(bool flag); |
| bool isNeuterable() const { return m_isNeuterable; } |
| |
| JS_EXPORT_PRIVATE virtual ~ArrayBufferView(); |
| |
| // Helper to verify byte offset is size aligned. |
| static bool verifyByteOffsetAlignment(unsigned byteOffset, size_t size) |
| { |
| return !(byteOffset & (size - 1)); |
| } |
| |
| // Helper to verify that a given sub-range of an ArrayBuffer is |
| // within range. |
| static bool verifySubRangeLength(const ArrayBuffer& buffer, unsigned byteOffset, unsigned numElements, size_t size) |
| { |
| unsigned byteLength = buffer.byteLength(); |
| if (byteOffset > byteLength) |
| return false; |
| unsigned remainingElements = (byteLength - byteOffset) / size; |
| if (numElements > remainingElements) |
| return false; |
| return true; |
| } |
| |
| virtual JSArrayBufferView* wrap(ExecState*, JSGlobalObject*) = 0; |
| |
| protected: |
| JS_EXPORT_PRIVATE ArrayBufferView(RefPtr<ArrayBuffer>&&, unsigned byteOffset); |
| |
| inline bool setImpl(ArrayBufferView*, unsigned byteOffset); |
| |
| // Caller passes in bufferByteLength to avoid a virtual function call. |
| inline bool setRangeImpl(const void* data, size_t dataByteLength, unsigned byteOffset, unsigned bufferByteLength); |
| inline bool getRangeImpl(void* destination, size_t dataByteLength, unsigned byteOffset, unsigned bufferByteLength); |
| |
| inline bool zeroRangeImpl(unsigned byteOffset, size_t rangeByteLength); |
| |
| static inline void calculateOffsetAndLength( |
| int start, int end, unsigned arraySize, |
| unsigned* offset, unsigned* length); |
| |
| // Input offset is in number of elements from this array's view; |
| // output offset is in number of bytes from the underlying buffer's view. |
| template <typename T> |
| static void clampOffsetAndNumElements( |
| const ArrayBuffer& buffer, |
| unsigned arrayByteOffset, |
| unsigned *offset, |
| unsigned *numElements) |
| { |
| unsigned maxOffset = (UINT_MAX - arrayByteOffset) / sizeof(T); |
| if (*offset > maxOffset) { |
| *offset = buffer.byteLength(); |
| *numElements = 0; |
| return; |
| } |
| *offset = arrayByteOffset + *offset * sizeof(T); |
| *offset = std::min(buffer.byteLength(), *offset); |
| unsigned remainingElements = (buffer.byteLength() - *offset) / sizeof(T); |
| *numElements = std::min(remainingElements, *numElements); |
| } |
| |
| // This is the address of the ArrayBuffer's storage, plus the byte offset. |
| CagedPtr<Gigacage::Primitive, void> m_baseAddress; |
| |
| unsigned m_byteOffset : 31; |
| bool m_isNeuterable : 1; |
| |
| private: |
| friend class ArrayBuffer; |
| RefPtr<ArrayBuffer> m_buffer; |
| }; |
| |
| bool ArrayBufferView::setImpl(ArrayBufferView* array, unsigned byteOffset) |
| { |
| if (byteOffset > byteLength() |
| || byteOffset + array->byteLength() > byteLength() |
| || byteOffset + array->byteLength() < byteOffset) { |
| // Out of range offset or overflow |
| return false; |
| } |
| |
| uint8_t* base = static_cast<uint8_t*>(baseAddress()); |
| memmove(base + byteOffset, array->baseAddress(), array->byteLength()); |
| return true; |
| } |
| |
| bool ArrayBufferView::setRangeImpl(const void* data, size_t dataByteLength, unsigned byteOffset, unsigned bufferByteLength) |
| { |
| // Do not replace with RELEASE_ASSERT; we want to avoid the virtual byteLength() function call in release. |
| ASSERT_WITH_SECURITY_IMPLICATION(bufferByteLength == byteLength()); |
| if (byteOffset > bufferByteLength |
| || byteOffset + dataByteLength > bufferByteLength |
| || byteOffset + dataByteLength < byteOffset) { |
| // Out of range offset or overflow |
| return false; |
| } |
| |
| uint8_t* base = static_cast<uint8_t*>(baseAddress()); |
| memmove(base + byteOffset, data, dataByteLength); |
| return true; |
| } |
| |
| bool ArrayBufferView::getRangeImpl(void* destination, size_t dataByteLength, unsigned byteOffset, unsigned bufferByteLength) |
| { |
| // Do not replace with RELEASE_ASSERT; we want to avoid the virtual byteLength() function call in release. |
| ASSERT_WITH_SECURITY_IMPLICATION(bufferByteLength == byteLength()); |
| if (byteOffset > bufferByteLength |
| || byteOffset + dataByteLength > bufferByteLength |
| || byteOffset + dataByteLength < byteOffset) { |
| // Out of range offset or overflow |
| return false; |
| } |
| |
| const uint8_t* base = static_cast<const uint8_t*>(baseAddress()); |
| memmove(destination, base + byteOffset, dataByteLength); |
| return true; |
| } |
| |
| bool ArrayBufferView::zeroRangeImpl(unsigned byteOffset, size_t rangeByteLength) |
| { |
| if (byteOffset > byteLength() |
| || byteOffset + rangeByteLength > byteLength() |
| || byteOffset + rangeByteLength < byteOffset) { |
| // Out of range offset or overflow |
| return false; |
| } |
| |
| uint8_t* base = static_cast<uint8_t*>(baseAddress()); |
| memset(base + byteOffset, 0, rangeByteLength); |
| return true; |
| } |
| |
| void ArrayBufferView::calculateOffsetAndLength( |
| int start, int end, unsigned arraySize, unsigned* offset, unsigned* length) |
| { |
| if (start < 0) |
| start += arraySize; |
| if (start < 0) |
| start = 0; |
| if (end < 0) |
| end += arraySize; |
| if (end < 0) |
| end = 0; |
| if (static_cast<unsigned>(end) > arraySize) |
| end = arraySize; |
| if (end < start) |
| end = start; |
| *offset = static_cast<unsigned>(start); |
| *length = static_cast<unsigned>(end - start); |
| } |
| |
| } // namespace JSC |
| |
| using JSC::ArrayBufferView; |