QScriptValue TypedArrayPrototype::subarray(qint32 begin) { TypedArray* typedArray = static_cast<TypedArray*>(parent()); QScriptValue arrayBuffer = thisObject().data().property(typedArray->_bufferName); qint32 byteOffset = thisObject().data().property(typedArray->_byteOffsetName).toInt32(); qint32 length = thisObject().data().property(typedArray->_lengthName).toInt32(); qint32 bytesPerElement = typedArray->_bytesPerElement; // if indices < 0 then they start from the end of the array begin = (begin < 0) ? length + begin : begin; // here we clamp the indices to fit the array begin = glm::clamp(begin, 0, (length - 1)); byteOffset += begin * bytesPerElement; return typedArray->newInstance(arrayBuffer, byteOffset, length - begin); }
QScriptValue TypedArrayPrototype::subarray(qint32 begin, qint32 end) { TypedArray* typedArray = static_cast<TypedArray*>(parent()); QScriptValue arrayBuffer = thisObject().data().property(typedArray->_bufferName); qint32 byteOffset = thisObject().data().property(typedArray->_byteOffsetName).toInt32(); qint32 length = thisObject().data().property(typedArray->_lengthName).toInt32(); qint32 bytesPerElement = typedArray->_bytesPerElement; // if indices < 0 then they start from the end of the array begin = (begin < 0) ? length + begin : begin; end = (end < 0) ? length + end : end; // here we clamp the indices to fit the array // note: begin offset is *inclusive* while end offset is *exclusive* // (see: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/subarray#Parameters) begin = glm::clamp(begin, 0, (length - 1)); end = glm::clamp(end, 0, length); byteOffset += begin * bytesPerElement; length = (end - begin > 0) ? end - begin : 0; return typedArray->newInstance(arrayBuffer, byteOffset, length); }
QScriptValue TypedArray::construct(QScriptContext* context, QScriptEngine* engine) { TypedArray* cls = qscriptvalue_cast<TypedArray*>(context->callee().data()); if (!cls) { return QScriptValue(); } if (context->argumentCount() == 0) { return cls->newInstance(0); } QScriptValue newObject; QScriptValue bufferArg = context->argument(0); QByteArray* arrayBuffer = qscriptvalue_cast<QByteArray*>(bufferArg.data()); // parse arguments if (arrayBuffer) { if (context->argumentCount() == 1) { // Case for entire ArrayBuffer newObject = cls->newInstance(bufferArg, 0, arrayBuffer->size()); } else { QScriptValue byteOffsetArg = context->argument(1); if (!byteOffsetArg.isNumber()) { engine->evaluate("throw \"ArgumentError: 2nd arg is not a number\""); return QScriptValue(); } if (byteOffsetArg.toInt32() < 0 || byteOffsetArg.toInt32() > arrayBuffer->size()) { engine->evaluate("throw \"RangeError: byteOffset out of range\""); return QScriptValue(); } if (byteOffsetArg.toInt32() % cls->_bytesPerElement != 0) { engine->evaluate("throw \"RangeError: byteOffset not a multiple of BYTES_PER_ELEMENT\""); } quint32 byteOffset = byteOffsetArg.toInt32(); if (context->argumentCount() == 2) { // case for end of ArrayBuffer if ((arrayBuffer->size() - byteOffset) % cls->_bytesPerElement != 0) { engine->evaluate("throw \"RangeError: byteLength - byteOffset not a multiple of BYTES_PER_ELEMENT\""); } quint32 length = (arrayBuffer->size() - byteOffset) / cls->_bytesPerElement; newObject = cls->newInstance(bufferArg, byteOffset, length); } else { QScriptValue lengthArg = (context->argumentCount() > 2) ? context->argument(2) : QScriptValue(); if (!lengthArg.isNumber()) { engine->evaluate("throw \"ArgumentError: 3nd arg is not a number\""); return QScriptValue(); } if (lengthArg.toInt32() < 0 || byteOffsetArg.toInt32() + lengthArg.toInt32() * (qint32)(cls->_bytesPerElement) > arrayBuffer->size()) { engine->evaluate("throw \"RangeError: byteLength out of range\""); return QScriptValue(); } quint32 length = lengthArg.toInt32(); // case for well-defined range newObject = cls->newInstance(bufferArg, byteOffset, length); } } } else if (context->argument(0).isNumber()) { // case for new ArrayBuffer newObject = cls->newInstance(context->argument(0).toInt32()); } else { newObject = cls->newInstance(bufferArg); } if (context->isCalledAsConstructor()) { // if called with the new keyword, replace this object context->setThisObject(newObject); return engine->undefinedValue(); } return newObject; }