blob: b5ff828de353e4c1f0c2abeab06db6617a46df94 [file] [log] [blame]
// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef COBALT_DOM_TYPED_ARRAY_H_
#define COBALT_DOM_TYPED_ARRAY_H_
#include "base/logging.h"
#include "base/stringprintf.h"
#include "cobalt/dom/array_buffer_view.h"
#include "cobalt/script/environment_settings.h"
#include "cobalt/script/exception_state.h"
#if defined(STARBOARD)
#include "starboard/memory.h"
#endif
namespace cobalt {
namespace dom {
// TypedArray serves as the base of all array interfaces defined in
// https://www.khronos.org/registry/typedarray/specs/latest/#7
// An array interfaces should inherit from TypedArray<ElementType>, for
// example, Uint8Array should inherit from TypedArray<uint8>. A macro
// named DEFINE_TYPED_ARRAY is provided at the end of this file for
// convenience.
template <typename ElementType>
class TypedArray : public ArrayBufferView {
public:
enum { kBytesPerElement = sizeof(ElementType) };
// Create a new TypedArray of the specified length. Each element is
// initialized to 0 inside the ctor of ArrayBuffer.
TypedArray(script::EnvironmentSettings* settings, uint32 length)
: ArrayBufferView(new ArrayBuffer(settings, length * kBytesPerElement)) {}
// Create a new TypedArray of the specified length and initialize it with the
// given data.
TypedArray(script::EnvironmentSettings* settings, const ElementType* data,
uint32 length)
: ArrayBufferView(new ArrayBuffer(settings, length * kBytesPerElement)) {
DCHECK_EQ(this->length(), length);
#if defined(STARBOARD)
SbMemoryCopy(this->data(), data, length * kBytesPerElement);
#else
memcpy(this->data(), data, length * kBytesPerElement);
#endif
}
// Creates a new TypedArray and copies the elements of 'other' into this.
TypedArray(script::EnvironmentSettings* settings,
const scoped_refptr<TypedArray>& other)
: ArrayBufferView(other->buffer()->Slice(settings, 0)) {}
// TODO: Support constructors from Array types.
// i.e. uint8[], float[], etc.
// Create a view on top of the specified buffer.
// Offset starts at byte_offset, length is byte_length.
// This refers to the same underlying data as buffer.
TypedArray(const scoped_refptr<ArrayBuffer>& buffer,
script::ExceptionState* exception_state)
: ArrayBufferView(buffer) {
if (buffer->byte_length() % kBytesPerElement != 0) {
exception_state->SetSimpleException(script::kRangeError,
kWrongByteLengthMultipleErrorFormat,
kBytesPerElement);
}
}
TypedArray(const scoped_refptr<ArrayBuffer>& buffer, uint32 byte_offset,
script::ExceptionState* exception_state)
: ArrayBufferView(buffer, byte_offset,
buffer->byte_length() - byte_offset) {
if (this->byte_offset() % kBytesPerElement != 0) {
exception_state->SetSimpleException(script::kRangeError,
kWrongByteOffsetMultipleErrorFormat,
kBytesPerElement);
} else if (buffer->byte_length() % kBytesPerElement != 0) {
exception_state->SetSimpleException(script::kRangeError,
kWrongByteLengthMultipleErrorFormat,
kBytesPerElement);
}
}
TypedArray(const scoped_refptr<ArrayBuffer>& buffer, uint32 byte_offset,
uint32 length, script::ExceptionState* exception_state)
: ArrayBufferView(buffer, byte_offset, length * kBytesPerElement) {
if (this->byte_offset() % kBytesPerElement != 0) {
exception_state->SetSimpleException(script::kRangeError,
kWrongByteOffsetMultipleErrorFormat,
kBytesPerElement);
} else if (byte_offset + length * kBytesPerElement >
buffer->byte_length()) {
exception_state->SetSimpleException(script::kInvalidLength);
}
}
// Create a new TypedArray that is a view into this existing array.
template <typename SubarrayType>
scoped_refptr<SubarrayType> SubarrayImpl(
script::EnvironmentSettings* settings, int start, int end) {
const int cur_length = static_cast<int>(length());
int clamped_start;
int clamped_end;
ArrayBuffer::ClampRange(start, end, cur_length, &clamped_start,
&clamped_end);
return new SubarrayType(
settings, buffer(),
static_cast<uint32>(byte_offset() + clamped_start * kBytesPerElement),
static_cast<uint32>(clamped_end - clamped_start), NULL);
}
// Copy items from 'source' into this array. This array must already be at
// least as large as 'source'.
void Set(const scoped_refptr<TypedArray>& source, uint32 offset,
script::ExceptionState* exception_state) {
if (offset >= length() || length() - offset < source->length()) {
exception_state->SetSimpleException(script::kInvalidLength);
return;
}
uint32 source_offset = 0;
while (source_offset < source->length()) {
#if defined(STARBOARD)
SbMemoryCopy(data() + offset, source->data() + source_offset,
sizeof(ElementType));
#else
memcpy(data() + offset, source->data() + source_offset,
sizeof(ElementType));
#endif
++offset;
++source_offset;
}
}
void Set(const scoped_refptr<TypedArray>& source,
script::ExceptionState* exception_state) {
Set(source, 0, exception_state);
}
// Write a single element of the array.
void Set(uint32 index, ElementType val) {
if (index < length()) {
#if defined(STARBOARD)
SbMemoryCopy(data() + index, &val, sizeof(ElementType));
#else
memcpy(data() + index, &val, sizeof(ElementType));
#endif
}
}
ElementType Get(uint32 index) const {
if (index < length()) {
ElementType val;
#if defined(STARBOARD)
SbMemoryCopy(&val, data() + index, sizeof(ElementType));
#else
memcpy(&val, data() + index, sizeof(ElementType));
#endif
return val;
} else {
// TODO: an out of bounds index should return undefined.
DLOG(ERROR) << "index " << index << " out of range " << length();
return 0;
}
}
uint32 length() const { return byte_length() / kBytesPerElement; }
const ElementType* data() const {
return reinterpret_cast<const ElementType*>(base_address());
}
ElementType* data() { return reinterpret_cast<ElementType*>(base_address()); }
DEFINE_WRAPPABLE_TYPE(TypedArray);
private:
DISALLOW_COPY_AND_ASSIGN(TypedArray);
};
} // namespace dom
} // namespace cobalt
// This macro is necessary as every SubarrayType should define its own wrappable
// type.
// Example usage:
// DEFINE_TYPED_ARRAY(Uint8Array, uint8);
// DEFINE_TYPED_ARRAY(Float32Array, float);
#define DEFINE_TYPED_ARRAY(SubarrayType, ElementType) \
class SubarrayType : public TypedArray<ElementType> { \
public: \
SubarrayType(script::EnvironmentSettings* settings, uint32 length, \
script::ExceptionState* exception_state) \
: TypedArray<ElementType>(settings, length) { \
UNREFERENCED_PARAMETER(exception_state); \
} \
SubarrayType(script::EnvironmentSettings* settings, \
const ElementType* data, uint32 length, \
script::ExceptionState* exception_state) \
: TypedArray<ElementType>(settings, data, length) { \
UNREFERENCED_PARAMETER(exception_state); \
} \
SubarrayType(script::EnvironmentSettings* settings, \
const scoped_refptr<SubarrayType>& other, \
script::ExceptionState* exception_state) \
: TypedArray<ElementType>(settings, other.get()) { \
UNREFERENCED_PARAMETER(exception_state); \
} \
SubarrayType(script::EnvironmentSettings* settings, \
const scoped_refptr<ArrayBuffer>& buffer, \
script::ExceptionState* exception_state) \
: TypedArray<ElementType>(buffer.get(), exception_state) { \
UNREFERENCED_PARAMETER(settings); \
} \
SubarrayType(script::EnvironmentSettings* settings, \
const scoped_refptr<ArrayBuffer>& buffer, uint32 byte_offset, \
script::ExceptionState* exception_state) \
: TypedArray<ElementType>(buffer, byte_offset, exception_state) { \
UNREFERENCED_PARAMETER(settings); \
} \
SubarrayType(script::EnvironmentSettings* settings, \
const scoped_refptr<ArrayBuffer>& buffer, uint32 byte_offset, \
uint32 byte_length, script::ExceptionState* exception_state) \
: TypedArray<ElementType>(buffer, byte_offset, byte_length, \
exception_state) { \
UNREFERENCED_PARAMETER(settings); \
} \
\
scoped_refptr<SubarrayType> Subarray( \
script::EnvironmentSettings* settings, int start, int end) { \
return SubarrayImpl<SubarrayType>(settings, start, end); \
} \
scoped_refptr<SubarrayType> Subarray( \
script::EnvironmentSettings* settings, int start) { \
return SubarrayImpl<SubarrayType>(settings, start, \
static_cast<int>(length())); \
} \
\
DEFINE_WRAPPABLE_TYPE(SubarrayType); \
\
private: \
DISALLOW_COPY_AND_ASSIGN(SubarrayType); \
};
#endif // COBALT_DOM_TYPED_ARRAY_H_