* Copyright (c) 2025 Huawei Device Co., Ltd.
* 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.
*/
#include "ecmascript/containers/containers_buffer.h"
#include <cstdint>
#include "ecmascript/base/typed_array_helper.h"
#include "ecmascript/base/typed_array_helper-inl.h"
#include "ecmascript/containers/containers_errors.h"
#include "ecmascript/ecma_string.h"
#include "ecmascript/ecma_string-inl.h"
#include "ecmascript/global_env.h"
#include "ecmascript/js_api/js_api_buffer.h"
#include "ecmascript/js_handle.h"
#include "ecmascript/js_hclass.h"
#include "ecmascript/js_tagged_number.h"
#include "ecmascript/js_tagged_value.h"
#include "ecmascript/js_typed_array.h"
#include "macros.h"
namespace panda::ecmascript::containers {
#define NEW_BUSINESS_ERROR_AND_THROW(thread, errorType, message) \
JSTaggedValue error = ContainerError::BusinessError(thread, errorType, message); \
THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception())
#define RANGE_ERROR_CHECK(value, name, left, right) \
double(num##name) = (value)->GetNumber(); \
if ((num##name) < (left) || (num##name) > (right)) { \
std::ostringstream oss; \
oss << "The value of \"" << (#name) << "\" is out of range. It must be >= " << (left) << " and <= " << (right) \
<< ". Received value is: " << (num##name); \
NEW_BUSINESS_ERROR_AND_THROW(thread, ErrorFlag::RANGE_ERROR, oss.str().c_str()); \
}
#define CHECK_NULL_OR_UNDEFINED(value) \
if ((value)->IsUndefined() || (value)->IsNull()) { \
NEW_BUSINESS_ERROR_AND_THROW(thread, ErrorFlag::TYPE_ERROR, "The parameter should not be null or undefined."); \
}
int32_t GetValueInt32(JSHandle<JSTaggedValue> valueHandle)
{
JSTaggedValue value = valueHandle.GetTaggedValue();
if (UNLIKELY(value.IsDouble())) {
return static_cast<int32_t>(
ecmascript::base::NumberHelper::DoubleToInt(value.GetDouble(), ecmascript::base::INT32_BITS));
}
return value.GetInt();
}
uint32_t GetValueUInt32(JSHandle<JSTaggedValue> valueHandle)
{
return static_cast<uint32_t>(GetValueInt32(valueHandle));
}
JSTypedArray *GetUInt8ArrayFromBufferObject(JSThread *thread, JSTaggedValue buffer)
{
if (buffer.IsJSUint8Array()) {
return JSTypedArray::Cast(buffer.GetTaggedObject());
}
ASSERT(buffer.IsJSAPIBuffer());
return JSTypedArray::Cast(
JSAPIFastBuffer::Cast(buffer.GetTaggedObject())->GetFastBufferData(thread).GetTaggedObject());
}
JSTypedArray *GetUInt8ArrayFromBufferObject(JSThread *thread, JSHandle<JSTaggedValue> buffer)
{
return GetUInt8ArrayFromBufferObject(thread, buffer.GetTaggedValue());
}
bool IsNegetiveNumber(JSHandle<JSTaggedValue> &v)
{
ASSERT(v->IsNumber());
return v->GetNumber() < 0;
}
bool IsValidEncoding(JSThread *thread, JSHandle<JSTaggedValue> &str)
{
if (!str->IsString()) {
return false;
}
auto strAccessor = EcmaStringAccessor(JSHandle<EcmaString>(str));
auto res = strAccessor.ToStdString(thread);
return JSAPIFastBuffer::GetEncodingType(res) != JSAPIFastBuffer::EncodingType::INVALID;
}
JSTaggedValue ContainersBuffer::BufferConstructor(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Buffer, Constructor);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> newTarget = GetNewTarget(argv);
JSHandle<JSTaggedValue> constructor = GetConstructor(argv);
if (newTarget->IsUndefined()) {
NEW_BUSINESS_ERROR_AND_THROW(thread, ErrorFlag::IS_NULL_ERROR,
"The Buffer's constructor cannot be directly invoked.");
}
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<JSAPIFastBuffer> bufferObj = JSHandle<JSAPIFastBuffer>::Cast(
factory->NewJSObjectByConstructor(JSHandle<JSFunction>(constructor), newTarget));
// parameter preprocess
JSHandle<JSTaggedValue> value = GetCallArg(argv, 0); // 0 means the first arg
JSHandle<JSTaggedValue> byteOffsetOrEncoding = GetCallArg(argv, 1); // 1 means the second arg
JSHandle<JSTaggedValue> length = GetCallArg(argv, 2); // 2 means the third arg
CHECK_NULL_OR_UNDEFINED(value);
if (value->IsNumber()) {
RANGE_ERROR_CHECK(value, value, 0, UINT32_MAX);
JSAPIFastBuffer::AllocateFastBuffer(thread, bufferObj, GetValueUInt32(value));
} else if (value->IsString()) {
if (!byteOffsetOrEncoding->IsString()) {
JSAPIFastBuffer::FromString(thread, bufferObj, value);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return bufferObj.GetTaggedValue();
}
JSAPIFastBuffer::FromString(thread, bufferObj, value, byteOffsetOrEncoding);
} else if (value->IsJSAPIBuffer()) {
if (byteOffsetOrEncoding->IsNumber() && length->IsNumber()) {
// share memory with another buffer(create from pool)
JSAPIFastBuffer::AllocateFromBufferObject(thread, bufferObj, value, GetValueUInt32(length),
GetValueUInt32(byteOffsetOrEncoding));
} else {
uint32_t bufferLength = JSAPIFastBuffer::Cast(value->GetTaggedObject())->GetLength();
JSAPIFastBuffer::AllocateFastBuffer(thread, bufferObj, bufferLength);
bufferObj->SetLength(bufferLength);
auto bufferHandle = JSHandle<JSTaggedValue>::Cast(bufferObj);
JSAPIFastBuffer::Copy(thread, bufferHandle, value, 0, 0, bufferLength);
}
} else if (value->IsArrayBuffer() || value->IsSharedArrayBuffer()) {
if (BuiltinsArrayBuffer::IsDetachedBuffer(thread, value.GetTaggedValue())) {
NEW_BUSINESS_ERROR_AND_THROW(thread, ErrorFlag::ARRAY_BUFFER_IS_NULL_OR_DETACHED,
"The underlying ArrayBuffer is null or detached.");
}
uint32_t index = 0;
uint32_t byteLength = JSHandle<JSArrayBuffer>(value)->GetArrayBufferByteLength();
if (byteOffsetOrEncoding->IsNumber()) {
RANGE_ERROR_CHECK(byteOffsetOrEncoding, byteOffsetOrEncoding, 0, UINT32_MAX);
index = GetValueUInt32(byteOffsetOrEncoding);
}
if (length->IsNumber()) {
RANGE_ERROR_CHECK(length, length, 0, UINT32_MAX);
byteLength = GetValueUInt32(length);
}
JSAPIFastBuffer::FromArrayBuffer(thread, bufferObj, value, index, byteLength);
} else if (value->IsJSUint8Array()) {
bufferObj->SetFastBufferData(thread, value);
bufferObj->SetLength(JSTypedArray::Cast(value->GetTaggedObject())->GetByteLength());
} else if (value->IsJSArray()) {
JSAPIFastBuffer::CreateBufferFromArrayLike(thread, bufferObj, value);
}
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return bufferObj.GetTaggedValue();
}
JSTaggedValue ContainersBuffer::GetSize(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Buffer, GetSize);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
CONTAINER_BUFFER_CHECK(GetSize);
return JSTaggedValue(JSHandle<JSAPIFastBuffer>::Cast(self)->GetLength());
}
JSTaggedValue ContainersBuffer::GetByteOffset(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Buffer, GetByteOffset);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
CONTAINER_BUFFER_CHECK(GetByteOffset);
return JSTaggedValue(JSHandle<JSAPIFastBuffer>::Cast(self)->GetOffset());
}
JSTaggedValue ContainersBuffer::GetArrayBuffer(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Buffer, GetArrayBuffer);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
CONTAINER_BUFFER_CHECK(GetArrayBuffer);
JSHandle<JSAPIFastBuffer> buffer = JSHandle<JSAPIFastBuffer>::Cast(self);
return JSAPIFastBuffer::GetArrayBuffer(thread, buffer);
}
JSTaggedValue ContainersBuffer::Compare(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Buffer, Compare);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
CONTAINER_BUFFER_CHECK(Compare);
auto value = GetCallArg(argv, 0); // 0 means the first arg
CHECK_NULL_OR_UNDEFINED(value);
if (!value->IsJSAPIBuffer() && !value->IsTypedArray()) {
NEW_BUSINESS_ERROR_AND_THROW(thread, ErrorFlag::TYPE_ERROR,
"The type of \"target\" must be [FastBuffer, Uint8Array].");
}
auto src = JSHandle<JSAPIFastBuffer>::Cast(self);
uint32_t tStart = 0;
uint32_t tEnd = (value->IsJSAPIBuffer() ? JSHandle<JSAPIFastBuffer>::Cast(value)->GetLength()
: JSHandle<JSTypedArray>::Cast(value)->GetByteLength());
uint32_t srcLength = src->GetLength();
uint32_t dstLength = tEnd;
auto targetStart = GetCallArg(argv, 1); // 1 means the second arg
auto targetEnd = GetCallArg(argv, 2); // 2 means the third arg
auto sourceStart = GetCallArg(argv, 3); // 3 means the fourth arg
auto sourceEnd = GetCallArg(argv, 4); // 4 means the fifth arg
if (targetStart->IsNumber()) {
RANGE_ERROR_CHECK(targetStart, targetStart, 0, dstLength);
tStart = GetValueUInt32(targetStart);
}
if (targetEnd->IsNumber()) {
RANGE_ERROR_CHECK(targetEnd, targetEnd, 0, dstLength);
tEnd = GetValueUInt32(targetEnd);
}
uint32_t sStart = 0;
if (sourceStart->IsNumber()) {
RANGE_ERROR_CHECK(sourceStart, sourceStart, 0, srcLength);
sStart = GetValueUInt32(sourceStart);
}
uint32_t sEnd = src->GetLength();
if (sourceEnd->IsNumber()) {
RANGE_ERROR_CHECK(sourceEnd, sourceEnd, 0, srcLength);
sEnd = GetValueUInt32(sourceEnd);
}
auto ret = JSAPIFastBuffer::Compare(thread, src, value, sStart, sEnd, tStart, tEnd);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return ret;
}
JSTaggedValue ContainersBuffer::Equals(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Buffer, Equals);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
CONTAINER_BUFFER_CHECK(Equals);
auto value = GetCallArg(argv, 0); // 0 means the first arg
CHECK_NULL_OR_UNDEFINED(value);
if (!value->IsJSAPIBuffer() && !value->IsTypedArray()) {
NEW_BUSINESS_ERROR_AND_THROW(thread, ErrorFlag::TYPE_ERROR,
"The type of \"target\" must be [FastBuffer, Uint8Array].");
}
auto src = JSHandle<JSAPIFastBuffer>::Cast(self);
uint32_t tEnd;
if (value->IsJSAPIBuffer()) {
tEnd = JSHandle<JSAPIFastBuffer>::Cast(value)->GetLength();
} else if (value->IsJSUint8Array()) {
tEnd = JSHandle<JSTypedArray>::Cast(value)->GetByteLength();
} else {
JSTaggedValue error = ContainerError::ParamError(thread, "unexpect value type, expect buffer | Uint8Array");
THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception());
}
auto res = JSAPIFastBuffer::Compare(thread, src, value, 0, src->GetLength(), 0, tEnd);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return res.GetInt() == 0 ? JSTaggedValue(true) : JSTaggedValue(false);
}
JSTaggedValue ContainersBuffer::IndexOf(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Buffer, IndexOf);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
CONTAINER_BUFFER_CHECK(IndexOf);
JSHandle<JSAPIFastBuffer> buffer = JSHandle<JSAPIFastBuffer>::Cast(self);
JSHandle<JSTaggedValue> value = GetCallArg(argv, 0); // 0 means the first arg
JSHandle<JSTaggedValue> offset = GetCallArg(argv, 1); // 1 means the second arg
CHECK_NULL_OR_UNDEFINED(value);
if (!value->IsString() && !value->IsJSAPIBuffer() && !value->IsTypedArray() && !value->IsNumber()) {
NEW_BUSINESS_ERROR_AND_THROW(thread, ErrorFlag::TYPE_ERROR,
"The type of \"value\" must be [string, FastBuffer, Uint8Array, number].");
}
int32_t offsetIndex = 0;
int32_t length = static_cast<int32_t>(buffer->GetLength());
if (offset->IsNumber()) {
if (offset->GetNumber() > length) {
return JSTaggedValue(-1);
} else {
offsetIndex = GetValueInt32(offset);
if (offsetIndex < 0) {
offsetIndex = -offsetIndex;
offsetIndex = length - offsetIndex;
offsetIndex = std::max(offsetIndex, 0);
}
}
}
JSHandle<JSTaggedValue> encoding = GetCallArg(argv, 2); // 2 means the third arg
JSAPIFastBuffer::EncodingType encodingType = JSAPIFastBuffer::UTF8;
if (encoding->IsString()) {
if (!IsValidEncoding(thread, encoding)) {
NEW_BUSINESS_ERROR_AND_THROW(thread, ErrorFlag::TYPE_ERROR,
"Parameter error. The type of \"encoding\" must be BufferEncoding. the encoding code is unknown");
}
encodingType = JSAPIFastBuffer::GetEncodingType(JSAPIFastBuffer::GetString(thread, encoding));
}
return JSAPIFastBuffer::IndexOf(thread, buffer, value, offsetIndex, encodingType);
}
JSTaggedValue ContainersBuffer::Includes(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Buffer, Includes);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
CONTAINER_BUFFER_CHECK(Includes);
JSHandle<JSAPIFastBuffer> buffer = JSHandle<JSAPIFastBuffer>::Cast(self);
JSHandle<JSTaggedValue> value = GetCallArg(argv, 0); // 0 means the first arg
CHECK_NULL_OR_UNDEFINED(value);
JSHandle<JSTaggedValue> offset = GetCallArg(argv, 1); // 1 means the second arg
int32_t length = static_cast<int32_t>(buffer->GetLength());
int32_t offsetIndex = 0;
if (offset->IsNumber()) {
if (offset->GetNumber() > length) {
return JSTaggedValue(false);
} else {
offsetIndex = GetValueInt32(offset);
if (offsetIndex < 0) {
offsetIndex = -offsetIndex;
offsetIndex = length - offsetIndex;
offsetIndex = std::max(offsetIndex, 0);
}
}
}
JSHandle<JSTaggedValue> encoding = GetCallArg(argv, 2); // 2 means the third arg
JSAPIFastBuffer::EncodingType encodingType = JSAPIFastBuffer::UTF8;
if (encoding->IsString()) {
if (!IsValidEncoding(thread, encoding)) {
NEW_BUSINESS_ERROR_AND_THROW(thread, ErrorFlag::TYPE_ERROR,
"Parameter error. The type of \"encoding\" must be BufferEncoding. the encoding code is unknown");
}
encodingType = JSAPIFastBuffer::GetEncodingType(JSAPIFastBuffer::GetString(thread, encoding));
}
return JSAPIFastBuffer::Includes(thread, buffer, value, offsetIndex, encodingType);
}
JSTaggedValue ContainersBuffer::LastIndexOf(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Buffer, LastIndexOf);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
CONTAINER_BUFFER_CHECK(LastIndexOf);
JSHandle<JSAPIFastBuffer> buffer = JSHandle<JSAPIFastBuffer>::Cast(self);
JSHandle<JSTaggedValue> value = GetCallArg(argv, 0); // 0 means the first arg
CHECK_NULL_OR_UNDEFINED(value);
if (!value->IsString() && !value->IsJSAPIBuffer() && !value->IsTypedArray() && !value->IsNumber()) {
NEW_BUSINESS_ERROR_AND_THROW(thread, ErrorFlag::TYPE_ERROR,
"The type of \"value\" must be [string, FastBuffer, Uint8Array, number].");
}
JSHandle<JSTaggedValue> offset = GetCallArg(argv, 1); // 1 means the second arg
int64_t length = static_cast<int64_t>(buffer->GetLength());
int64_t offsetIndex = length - 1;
if (offset->IsNumber()) {
if (offset->GetNumber() < -length) {
return JSTaggedValue(-1);
}
if (offset->GetNumber() < length) {
offsetIndex = GetValueInt32(offset);
if (offsetIndex < 0) {
offsetIndex = -offsetIndex;
offsetIndex = length - offsetIndex;
}
}
}
offsetIndex = std::min(offsetIndex, length);
JSHandle<JSTaggedValue> encoding = GetCallArg(argv, 2); // 2 means the third arg
JSAPIFastBuffer::EncodingType encodingType = JSAPIFastBuffer::UTF8;
if (encoding->IsString()) {
if (!IsValidEncoding(thread, encoding)) {
NEW_BUSINESS_ERROR_AND_THROW(thread, ErrorFlag::TYPE_ERROR,
"Parameter error. The type of \"encoding\" must be BufferEncoding. the encoding code is unknown");
}
encodingType = JSAPIFastBuffer::GetEncodingType(JSAPIFastBuffer::GetString(thread, encoding));
}
return JSAPIFastBuffer::IndexOf(thread, buffer, value, static_cast<uint32_t>(offsetIndex), encodingType, true);
}
JSTaggedValue ContainersBuffer::Entries(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Buffer, Entries);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
CONTAINER_BUFFER_CHECK(Entries);
JSHandle<JSAPIFastBuffer> buffer = JSHandle<JSAPIFastBuffer>::Cast(self);
return JSAPIFastBuffer::Entries(thread, buffer);
}
JSTaggedValue ContainersBuffer::Keys(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Buffer, Keys);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
CONTAINER_BUFFER_CHECK(Keys);
JSHandle<JSAPIFastBuffer> buffer = JSHandle<JSAPIFastBuffer>::Cast(self);
return JSAPIFastBuffer::Keys(thread, buffer);
}
JSTaggedValue ContainersBuffer::Values(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Buffer, Values);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
CONTAINER_BUFFER_CHECK(Values);
JSHandle<JSAPIFastBuffer> buffer = JSHandle<JSAPIFastBuffer>::Cast(self);
return JSAPIFastBuffer::Values(thread, buffer);
}
JSTaggedValue ContainersBuffer::Fill(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Buffer, Fill);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
CONTAINER_BUFFER_CHECK(Fill);
JSHandle<JSAPIFastBuffer> buffer = JSHandle<JSAPIFastBuffer>::Cast(self);
JSHandle<JSTaggedValue> value = GetCallArg(argv, 0); // 0 means the first arg
uint32_t length = buffer->GetLength();
if (length == 0 || value->IsUndefinedOrNull()) {
return buffer.GetTaggedValue();
}
if (!value->IsString() && !value->IsJSAPIBuffer() && !value->IsTypedArray() && !value->IsNumber()) {
NEW_BUSINESS_ERROR_AND_THROW(thread, ErrorFlag::TYPE_ERROR,
"The type of \"value\" must be [string, FastBuffer, Uint8Array, number].");
}
JSHandle<JSTaggedValue> start = GetCallArg(argv, 1); // 1 means the second arg
if (start->IsString()) {
JSAPIFastBuffer::EncodingType encodingType = JSAPIFastBuffer::UTF8;
if (!IsValidEncoding(thread, start)) {
NEW_BUSINESS_ERROR_AND_THROW(thread, ErrorFlag::TYPE_ERROR,
"Parameter error. The type of \"encoding\" must be BufferEncoding. the encoding code is unknown");
}
encodingType = JSAPIFastBuffer::GetEncodingType(JSAPIFastBuffer::GetString(thread, start));
return JSAPIFastBuffer::Fill(thread, buffer, value, encodingType, 0, length);
}
JSHandle<JSTaggedValue> end = GetCallArg(argv, 2); // 2 means the third arg
JSHandle<JSTaggedValue> encoding = GetCallArg(argv, 3); // 3 means the fourth arg
uint32_t startIndex = 0;
if (start->IsNumber()) {
RANGE_ERROR_CHECK(start, offset, 0, length);
startIndex = GetValueUInt32(start);
}
uint32_t endIndex = length;
if (end->IsNumber()) {
RANGE_ERROR_CHECK(end, end, 0, length);
endIndex = GetValueUInt32(end);
}
if (endIndex <= startIndex) {
return buffer.GetTaggedValue();
}
JSAPIFastBuffer::EncodingType encodingType = JSAPIFastBuffer::UTF8;
if (encoding->IsString()) {
if (!IsValidEncoding(thread, encoding)) {
NEW_BUSINESS_ERROR_AND_THROW(thread, ErrorFlag::TYPE_ERROR,
"Parameter error. The type of \"encoding\" must be BufferEncoding. the encoding code is unknown");
}
encodingType = JSAPIFastBuffer::GetEncodingType(JSAPIFastBuffer::GetString(thread, encoding));
}
return JSAPIFastBuffer::Fill(thread, buffer, value, encodingType, startIndex, endIndex);
}
JSTaggedValue ContainersBuffer::Write(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Buffer, Write);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
CONTAINER_BUFFER_CHECK(Write);
JSAPIFastBuffer::EncodingType encodingType = JSAPIFastBuffer::UTF8;
JSHandle<JSAPIFastBuffer> buffer = JSHandle<JSAPIFastBuffer>::Cast(self);
JSHandle<JSTaggedValue> firstArg = GetCallArg(argv, 0); // 0 means the first arg
CHECK_NULL_OR_UNDEFINED(firstArg);
if (firstArg->IsString()) {
JSHandle<JSTaggedValue> secondArg = GetCallArg(argv, 1); // 1 means the second arg
if (secondArg->IsUndefined() || secondArg->IsNull()) {
return JSAPIFastBuffer::WriteString(thread, buffer, firstArg, 0, buffer->GetLength(),
JSAPIFastBuffer::UTF8);
}
if (secondArg->IsString()) {
if (!IsValidEncoding(thread, secondArg)) {
NEW_BUSINESS_ERROR_AND_THROW(thread, ErrorFlag::TYPE_ERROR,
"Parameter error. The type of \"encoding\" must be BufferEncoding. the encoding code is unknown");
}
encodingType = JSAPIFastBuffer::GetEncodingType(JSAPIFastBuffer::GetString(thread, secondArg));
return JSAPIFastBuffer::WriteString(thread, buffer, firstArg, 0, buffer->GetLength(), encodingType);
}
uint32_t offset = 0;
if (secondArg->IsNumber()) {
RANGE_ERROR_CHECK(secondArg, offset, 0, buffer->GetLength() - 1);
offset = GetValueUInt32(secondArg);
}
uint32_t maxLength = buffer->GetLength() - offset;
JSHandle<JSTaggedValue> thirdArg = GetCallArg(argv, 2); // 2 means the third arg
if (thirdArg->IsNumber()) {
RANGE_ERROR_CHECK(thirdArg, length, 0, maxLength);
maxLength = std::min(GetValueUInt32(thirdArg), maxLength);
}
JSHandle<JSTaggedValue> encoding = GetCallArg(argv, 3); // 3 means the fourth arg
if (encoding->IsString()) {
if (!IsValidEncoding(thread, encoding)) {
NEW_BUSINESS_ERROR_AND_THROW(thread, ErrorFlag::TYPE_ERROR,
"Parameter error. The type of \"encoding\" must be BufferEncoding. the encoding code is unknown");
}
encodingType = JSAPIFastBuffer::GetEncodingType(JSAPIFastBuffer::GetString(thread, encoding));
}
return JSAPIFastBuffer::WriteString(thread, buffer, firstArg, offset, maxLength, encodingType);
}
return JSTaggedValue(0);
}
JSTaggedValue ContainersBuffer::ToString(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Buffer, ToString);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
CONTAINER_BUFFER_CHECK(ToString);
JSHandle<JSAPIFastBuffer> buffer = JSHandle<JSAPIFastBuffer>::Cast(self);
JSHandle<JSTaggedValue> firstArg = GetCallArg(argv, 0); // 0 means the first arg
JSHandle<JSTaggedValue> start = GetCallArg(argv, 1); // 1 means the second arg
JSHandle<JSTaggedValue> end = GetCallArg(argv, 2); // 2 means the third arg
JSAPIFastBuffer::EncodingType encodingType = JSAPIFastBuffer::UTF8;
if (firstArg->IsString()) {
if (!IsValidEncoding(thread, firstArg)) {
NEW_BUSINESS_ERROR_AND_THROW(thread, ErrorFlag::TYPE_ERROR,
"Parameter error. The type of \"encoding\" must be BufferEncoding. the encoding code is unknown");
}
encodingType = JSAPIFastBuffer::GetEncodingType(thread, firstArg);
}
uint32_t startIndex = 0;
if (start->IsNumber()) {
if (start->GetNumber() < 0) {
startIndex = 0;
} else {
startIndex = GetValueUInt32(start);
}
}
uint32_t endIndex = buffer->GetLength();
if (startIndex >= endIndex) {
return thread->GlobalConstants()->GetHandledEmptyString().GetTaggedValue();
}
if (end->IsNumber()) {
if (end->GetNumber() <= startIndex) {
return thread->GlobalConstants()->GetHandledEmptyString().GetTaggedValue();
}
if (end->GetNumber() < endIndex) {
endIndex = GetValueUInt32(end);
}
}
auto ret = JSAPIFastBuffer::ToString(thread, buffer, encodingType, startIndex, endIndex);
return ret;
}
JSTaggedValue ContainersBuffer::Copy(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Buffer, Copy);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
CONTAINER_BUFFER_CHECK(Copy);
JSHandle<JSAPIFastBuffer> src = JSHandle<JSAPIFastBuffer>::Cast(self);
JSHandle<JSTaggedValue> dst = GetCallArg(argv, 0); // 0 means the first arg
CHECK_NULL_OR_UNDEFINED(dst);
if (!dst->IsJSAPIBuffer() && !dst->IsTypedArray()) {
NEW_BUSINESS_ERROR_AND_THROW(thread, ErrorFlag::TYPE_ERROR,
"The type of \"target\" must be [FastBuffer, Uint8Array].");
}
uint32_t srcLength = src->GetLength();
uint32_t dstLength = dst->IsJSAPIBuffer() ? JSHandle<JSAPIFastBuffer>(dst)->GetLength()
: JSHandle<JSTypedArray>(dst)->GetArrayLength();
auto targetStart = GetCallArg(argv, 1); // 1 means the second arg
auto sourceStart = GetCallArg(argv, 2); // 2 means the third arg
auto sourceEnd = GetCallArg(argv, 3); // 3 means the fourth arg
uint32_t tStart = 0;
if (targetStart->IsNumber()) {
RANGE_ERROR_CHECK(targetStart, targetStart, 0, UINT32_MAX);
tStart = GetValueUInt32(targetStart);
}
uint32_t sStart = 0;
if (sourceStart->IsNumber()) {
RANGE_ERROR_CHECK(sourceStart, sourceStart, 0, UINT32_MAX);
sStart = GetValueUInt32(sourceStart);
}
uint32_t sEnd = srcLength;
if (sourceEnd->IsNumber()) {
RANGE_ERROR_CHECK(sourceEnd, sourceEnd, 0, srcLength);
sEnd = GetValueUInt32(sourceEnd);
}
if (sEnd <= sStart || sStart >= srcLength || tStart >= dstLength) {
return JSTaggedValue(0);
}
return JSAPIFastBuffer::Copy(thread, dst, self, tStart, sStart, sEnd);
}
JSTaggedValue ContainersBuffer::WriteUIntBE(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv != nullptr);
JSThread *thread = argv->GetThread();
BUILTINS_API_TRACE(thread, Buffer, WriteUIntBE);
[[maybe_unused]] EcmaHandleScope handleScope(thread);
CONTAINER_BUFFER_CHECK(WriteUIntBE);
JSHandle<JSAPIFastBuffer> buffer = JSHandle<JSAPIFastBuffer>::Cast(self);
JSHandle<JSTaggedValue> value = GetCallArg(argv, 0); // 0 means the first arg
JSHandle<JSTaggedValue> offset = GetCallArg(argv, 1); // 1 means the second arg
JSHandle<JSTaggedValue> byteLengthHandle = GetCallArg(argv, 2); // 2 means the third arg
CHECK_NULL_OR_UNDEFINED(value);
if (!value->IsNumber() || !byteLengthHandle->IsNumber()) {
NEW_BUSINESS_ERROR_AND_THROW(thread, ErrorFlag::TYPE_ERROR, "The parameter should be number.");
}
RANGE_ERROR_CHECK(byteLengthHandle, byteLength, 0, buffer->GetLength());
uint32_t byteLength = GetValueUInt32(byteLengthHandle);
uint32_t offsetIndex = 0;
if (offset->IsNumber()) {
RANGE_ERROR_CHECK(offset, offset, 0, buffer->GetLength() - byteLength);
offsetIndex = GetValueUInt32(offset);
}
auto ret = JSAPIFastBuffer::WriteBytesValue(thread, buffer, value, offsetIndex,
static_cast<JSAPIFastBuffer::ByteLength>(byteLength));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return ret;
}
JSTaggedValue ContainersBuffer::WriteUIntLE(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv != nullptr);
JSThread *thread = argv->GetThread();
BUILTINS_API_TRACE(thread, Buffer, WriteUIntLE);
[[maybe_unused]] EcmaHandleScope handleScope(thread);
CONTAINER_BUFFER_CHECK(WriteUIntLE)
JSHandle<JSAPIFastBuffer> buffer = JSHandle<JSAPIFastBuffer>::Cast(self);
JSHandle<JSTaggedValue> value = GetCallArg(argv, 0); // 0 means the first arg
JSHandle<JSTaggedValue> offset = GetCallArg(argv, 1); // 1 means the second arg
JSHandle<JSTaggedValue> byteLengthHandle = GetCallArg(argv, 2); // 2 means the third arg
if (!value->IsNumber() || !byteLengthHandle->IsNumber()) {
NEW_BUSINESS_ERROR_AND_THROW(thread, ErrorFlag::TYPE_ERROR, "The parameter should be number.");
}
RANGE_ERROR_CHECK(byteLengthHandle, byteLength, 0, buffer->GetLength());
uint32_t byteLength = GetValueUInt32(byteLengthHandle);
CHECK_NULL_OR_UNDEFINED(value);
uint32_t offsetIndex = 0;
if (offset->IsNumber()) {
RANGE_ERROR_CHECK(offset, offset, 0, buffer->GetLength() - byteLength);
offsetIndex = GetValueUInt32(offset);
}
auto ret = JSAPIFastBuffer::WriteBytesValue(thread, buffer, value, offsetIndex,
static_cast<JSAPIFastBuffer::ByteLength>(byteLength), true);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return ret;
}
JSTaggedValue ContainersBuffer::ReadUIntBE(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv != nullptr);
JSThread *thread = argv->GetThread();
BUILTINS_API_TRACE(thread, Buffer, ReadUIntBE);
[[maybe_unused]] EcmaHandleScope handleScope(thread);
CONTAINER_BUFFER_CHECK(ReadUIntBE)
JSHandle<JSAPIFastBuffer> buffer = JSHandle<JSAPIFastBuffer>::Cast(self);
JSHandle<JSTaggedValue> offset = GetCallArg(argv, 0); // 0 means the first arg
JSHandle<JSTaggedValue> byteLengthHandle = GetCallArg(argv, 1); // 1 means the second arg
CHECK_NULL_OR_UNDEFINED(offset);
if (!byteLengthHandle->IsNumber()) {
NEW_BUSINESS_ERROR_AND_THROW(thread, ErrorFlag::TYPE_ERROR, "The byteLength should be number.");
}
RANGE_ERROR_CHECK(byteLengthHandle, byteLength, 0, buffer->GetLength());
uint32_t byteLength = GetValueUInt32(byteLengthHandle);
uint32_t offsetIndex = 0;
if (offset->IsNumber()) {
RANGE_ERROR_CHECK(offset, offset, 0, buffer->GetLength() - byteLength);
offsetIndex = GetValueUInt32(offset);
}
auto ret =
JSAPIFastBuffer::ReadBytes(thread, buffer, offsetIndex, static_cast<JSAPIFastBuffer::ByteLength>(byteLength));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return ret;
}
JSTaggedValue ContainersBuffer::ReadUIntLE(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv != nullptr);
JSThread *thread = argv->GetThread();
BUILTINS_API_TRACE(thread, Buffer, ReadUIntLE);
[[maybe_unused]] EcmaHandleScope handleScope(thread);
CONTAINER_BUFFER_CHECK(ReadUIntLE)
JSHandle<JSAPIFastBuffer> buffer = JSHandle<JSAPIFastBuffer>::Cast(self);
JSHandle<JSTaggedValue> offset = GetCallArg(argv, 0); // 0 means the first arg
JSHandle<JSTaggedValue> byteLengthHandle = GetCallArg(argv, 1); // 1 means the second arg
CHECK_NULL_OR_UNDEFINED(offset);
if (!byteLengthHandle->IsNumber()) {
NEW_BUSINESS_ERROR_AND_THROW(thread, ErrorFlag::TYPE_ERROR, "The byteLength should be number.");
}
RANGE_ERROR_CHECK(byteLengthHandle, byteLength, 0, buffer->GetLength());
uint32_t byteLength = GetValueUInt32(byteLengthHandle);
uint32_t offsetIndex = 0;
if (offset->IsNumber()) {
RANGE_ERROR_CHECK(offset, offset, 0, buffer->GetLength() - byteLength);
offsetIndex = GetValueUInt32(offset);
}
auto ret = JSAPIFastBuffer::ReadBytes(thread, buffer, offsetIndex,
static_cast<JSAPIFastBuffer::ByteLength>(byteLength), true);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return ret;
}
JSTaggedValue ContainersBuffer::WriteIntBE(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv != nullptr);
JSThread *thread = argv->GetThread();
BUILTINS_API_TRACE(thread, Buffer, WriteIntBE);
[[maybe_unused]] EcmaHandleScope handleScope(thread);
CONTAINER_BUFFER_CHECK(WriteIntBE)
JSHandle<JSAPIFastBuffer> buffer = JSHandle<JSAPIFastBuffer>::Cast(self);
JSHandle<JSTaggedValue> value = GetCallArg(argv, 0); // 0 means the first arg
JSHandle<JSTaggedValue> offset = GetCallArg(argv, 1); // 1 means the second arg
JSHandle<JSTaggedValue> byteLengthHandle = GetCallArg(argv, 2); // 2 means the third arg
CHECK_NULL_OR_UNDEFINED(value);
if (!value->IsNumber() || !byteLengthHandle->IsNumber()) {
NEW_BUSINESS_ERROR_AND_THROW(thread, ErrorFlag::TYPE_ERROR, "The parameter should be number.");
}
RANGE_ERROR_CHECK(byteLengthHandle, byteLength, 0, buffer->GetLength());
uint32_t byteLength = GetValueUInt32(byteLengthHandle);
uint32_t offsetIndex = 0;
if (offset->IsNumber()) {
RANGE_ERROR_CHECK(offset, offset, 0, buffer->GetLength() - byteLength);
offsetIndex = GetValueUInt32(offset);
}
auto ret = JSAPIFastBuffer::WriteBytesValue(thread, buffer, value, offsetIndex,
static_cast<JSAPIFastBuffer::ByteLength>(byteLength));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return ret;
}
JSTaggedValue ContainersBuffer::WriteIntLE(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv != nullptr);
JSThread *thread = argv->GetThread();
BUILTINS_API_TRACE(thread, Buffer, WriteIntLE);
[[maybe_unused]] EcmaHandleScope handleScope(thread);
CONTAINER_BUFFER_CHECK(WriteIntLE)
JSHandle<JSAPIFastBuffer> buffer = JSHandle<JSAPIFastBuffer>::Cast(self);
JSHandle<JSTaggedValue> value = GetCallArg(argv, 0); // 0 means the first arg
JSHandle<JSTaggedValue> offset = GetCallArg(argv, 1); // 1 means the second arg
JSHandle<JSTaggedValue> byteLengthHandle = GetCallArg(argv, 2); // 2 means the third arg
CHECK_NULL_OR_UNDEFINED(value);
if (!value->IsNumber() || !byteLengthHandle->IsNumber()) {
NEW_BUSINESS_ERROR_AND_THROW(thread, ErrorFlag::TYPE_ERROR, "The parameter should be number.");
}
RANGE_ERROR_CHECK(byteLengthHandle, byteLength, 0, buffer->GetLength());
uint32_t byteLength = GetValueUInt32(byteLengthHandle);
uint32_t offsetIndex = 0;
if (offset->IsNumber()) {
RANGE_ERROR_CHECK(offset, offset, 0, buffer->GetLength() - byteLength);
offsetIndex = GetValueUInt32(offset);
}
auto ret = JSAPIFastBuffer::WriteBytesValue(thread, buffer, value, offsetIndex,
static_cast<JSAPIFastBuffer::ByteLength>(byteLength), true);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return ret;
}
JSTaggedValue ContainersBuffer::ReadIntBE(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv != nullptr);
JSThread *thread = argv->GetThread();
BUILTINS_API_TRACE(thread, Buffer, ReadIntBE);
[[maybe_unused]] EcmaHandleScope handleScope(thread);
CONTAINER_BUFFER_CHECK(ReadIntBE)
JSHandle<JSAPIFastBuffer> buffer = JSHandle<JSAPIFastBuffer>::Cast(self);
JSHandle<JSTaggedValue> offset = GetCallArg(argv, 0); // 0 means the first arg
JSHandle<JSTaggedValue> byteLengthHandle = GetCallArg(argv, 1); // 1 means the second arg
CHECK_NULL_OR_UNDEFINED(offset);
if (!byteLengthHandle->IsNumber()) {
NEW_BUSINESS_ERROR_AND_THROW(thread, ErrorFlag::TYPE_ERROR, "The byteLength should be number.");
}
RANGE_ERROR_CHECK(byteLengthHandle, byteLength, 0, buffer->GetLength());
uint32_t byteLength = GetValueUInt32(byteLengthHandle);
uint32_t offsetIndex = 0;
if (offset->IsNumber()) {
RANGE_ERROR_CHECK(offset, offset, 0, buffer->GetLength() - byteLength);
offsetIndex = GetValueUInt32(offset);
}
auto ret =
JSAPIFastBuffer::ReadInt(thread, buffer, offsetIndex, static_cast<JSAPIFastBuffer::ByteLength>(byteLength));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return ret;
}
JSTaggedValue ContainersBuffer::ReadIntLE(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv != nullptr);
JSThread *thread = argv->GetThread();
BUILTINS_API_TRACE(thread, Buffer, ReadIntLE);
[[maybe_unused]] EcmaHandleScope handleScope(thread);
CONTAINER_BUFFER_CHECK(ReadIntLE)
JSHandle<JSAPIFastBuffer> buffer = JSHandle<JSAPIFastBuffer>::Cast(self);
JSHandle<JSTaggedValue> offset = GetCallArg(argv, 0); // 0 means the first arg
JSHandle<JSTaggedValue> byteLengthHandle = GetCallArg(argv, 1); // 1 means the second arg
CHECK_NULL_OR_UNDEFINED(offset);
if (!byteLengthHandle->IsNumber()) {
NEW_BUSINESS_ERROR_AND_THROW(thread, ErrorFlag::TYPE_ERROR, "The byteLength should be number.");
}
RANGE_ERROR_CHECK(byteLengthHandle, byteLength, 0, buffer->GetLength());
uint32_t byteLength = GetValueUInt32(byteLengthHandle);
uint32_t offsetIndex = 0;
if (offset->IsNumber()) {
RANGE_ERROR_CHECK(offset, offset, 0, buffer->GetLength() - byteLength);
offsetIndex = GetValueUInt32(offset);
}
auto ret = JSAPIFastBuffer::ReadInt(thread, buffer, offsetIndex,
static_cast<JSAPIFastBuffer::ByteLength>(byteLength), true);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return ret;
}
JSTaggedValue ContainersBuffer::WriteUInt8(EcmaRuntimeCallInfo *argv)
{
return ContainersBuffer::WriteUInt8LE(argv);
}
JSTaggedValue ContainersBuffer::WriteInt8(EcmaRuntimeCallInfo *argv)
{
return ContainersBuffer::WriteInt8LE(argv);
}
JSTaggedValue ContainersBuffer::ReadUInt8(EcmaRuntimeCallInfo *argv)
{
return ContainersBuffer::ReadUInt8LE(argv);
}
JSTaggedValue ContainersBuffer::ReadInt8(EcmaRuntimeCallInfo *argv)
{
return ContainersBuffer::ReadInt8LE(argv);
}
CONTAINER_BUFFER_ACCESSORS(UInt8)
CONTAINER_BUFFER_ACCESSORS(UInt16)
CONTAINER_BUFFER_ACCESSORS(UInt32)
CONTAINER_BUFFER_ACCESSORS(BigUInt64)
CONTAINER_BUFFER_ACCESSORS(Int8)
CONTAINER_BUFFER_ACCESSORS(Int16)
CONTAINER_BUFFER_ACCESSORS(Int32)
CONTAINER_BUFFER_ACCESSORS(BigInt64)
CONTAINER_BUFFER_ACCESSORS(Float32)
CONTAINER_BUFFER_ACCESSORS(Float64)
} // namespace panda::ecmascript::containers