blob: 099954b86533f7320b5639526079f3e2a08af50e [file] [log] [blame]
/*
* Copyright (C) 2009-2021 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/CheckedArithmetic.h>
#include <wtf/RefCounted.h>
#include <wtf/RefPtr.h>
namespace JSC {
class JSArrayBufferView;
class JSGlobalObject;
class CallFrame;
class ArrayBufferView : public RefCounted<ArrayBufferView> {
public:
virtual TypedArrayType getType() const = 0;
bool isDetached() const
{
return !m_buffer || m_buffer->isDetached();
}
RefPtr<ArrayBuffer> possiblySharedBuffer() const
{
if (isDetached())
return nullptr;
return m_buffer;
}
RefPtr<ArrayBuffer> unsharedBuffer() const
{
RefPtr<ArrayBuffer> result = possiblySharedBuffer();
RELEASE_ASSERT(!result->isShared());
return result;
}
bool isShared() const
{
if (isDetached())
return false;
return m_buffer->isShared();
}
void* baseAddress() const
{
if (isDetached())
return nullptr;
return m_baseAddress.getMayBeNull(byteLength());
}
void* data() const { return baseAddress(); }
size_t byteOffset() const
{
if (isDetached())
return 0;
return m_byteOffset;
}
size_t byteLength() const { return m_byteLength; }
JS_EXPORT_PRIVATE void setDetachable(bool);
bool isDetachable() const { return m_isDetachable; }
JS_EXPORT_PRIVATE virtual ~ArrayBufferView();
// Helper to verify byte offset is size aligned.
static bool verifyByteOffsetAlignment(size_t byteOffset, size_t elementSize)
{
return !(byteOffset & (elementSize - 1));
}
// Helper to verify that a given sub-range of an ArrayBuffer is within range.
static bool verifySubRangeLength(const ArrayBuffer& buffer, size_t byteOffset, size_t numElements, unsigned elementSize)
{
size_t byteLength = buffer.byteLength();
if (byteOffset > byteLength)
return false;
size_t remainingElements = (byteLength - byteOffset) / static_cast<size_t>(elementSize);
if (numElements > remainingElements)
return false;
return true;
}
virtual JSArrayBufferView* wrap(JSGlobalObject*, JSGlobalObject*) = 0;
protected:
JS_EXPORT_PRIVATE ArrayBufferView(RefPtr<ArrayBuffer>&&, size_t byteOffset, size_t byteLength);
inline bool setImpl(ArrayBufferView*, size_t byteOffset);
inline bool setRangeImpl(const void* data, size_t dataByteLength, size_t byteOffset);
inline bool getRangeImpl(void* destination, size_t dataByteLength, size_t byteOffset);
inline bool zeroRangeImpl(size_t byteOffset, size_t rangeByteLength);
// 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,
size_t arrayByteOffset,
size_t *offset,
size_t *numElements)
{
size_t maxOffset = (std::numeric_limits<size_t>::max() - arrayByteOffset) / sizeof(T);
if (*offset > maxOffset) {
*offset = buffer.byteLength();
*numElements = 0;
return;
}
CheckedSize adjustedOffset = *offset;
adjustedOffset *= sizeof(T);
adjustedOffset += arrayByteOffset;
if (adjustedOffset.hasOverflowed() || adjustedOffset.value() > buffer.byteLength())
*offset = buffer.byteLength();
else
*offset = adjustedOffset.value();
size_t remainingElements = (buffer.byteLength() - *offset) / sizeof(T);
*numElements = std::min(remainingElements, *numElements);
}
#if USE(LARGE_TYPED_ARRAYS)
uint64_t m_byteOffset : 63;
#else
uint32_t m_byteOffset : 31;
#endif
bool m_isDetachable : 1;
UCPURegister m_byteLength;
using BaseAddress = CagedPtr<Gigacage::Primitive, void, tagCagedPtr>;
// This is the address of the ArrayBuffer's storage, plus the byte offset.
BaseAddress m_baseAddress;
private:
friend class ArrayBuffer;
RefPtr<ArrayBuffer> m_buffer;
};
bool ArrayBufferView::setImpl(ArrayBufferView* array, size_t byteOffset)
{
if (!isSumSmallerThanOrEqual(byteOffset, array->byteLength(), byteLength()))
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, size_t byteOffset)
{
if (!isSumSmallerThanOrEqual(byteOffset, dataByteLength, byteLength()))
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, size_t byteOffset)
{
if (!isSumSmallerThanOrEqual(byteOffset, dataByteLength, byteLength()))
return false;
const uint8_t* base = static_cast<const uint8_t*>(baseAddress());
memmove(destination, base + byteOffset, dataByteLength);
return true;
}
bool ArrayBufferView::zeroRangeImpl(size_t byteOffset, size_t rangeByteLength)
{
if (!isSumSmallerThanOrEqual(byteOffset, rangeByteLength, byteLength()))
return false;
uint8_t* base = static_cast<uint8_t*>(baseAddress());
memset(base + byteOffset, 0, rangeByteLength);
return true;
}
} // namespace JSC
using JSC::ArrayBufferView;