* 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.
*/
#ifndef ECMASCRIPT_JS_API_JS_API_BUFFER_H
#define ECMASCRIPT_JS_API_JS_API_BUFFER_H
#include "ecmascript/base/atomic_helper.h"
#include "ecmascript/ecma_string.h"
#include "ecmascript/js_handle.h"
#include "ecmascript/js_object.h"
#include "ecmascript/containers/containers_errors.h"
#include "ecmascript/js_tagged_value-inl.h"
#include "ecmascript/js_tagged_value.h"
#include "ecmascript/js_typed_array.h"
#include "ecmascript/base/typed_array_helper-inl.h"
#include <iomanip>
#include <ios>
#include <string_view>
namespace panda::ecmascript {
* Provide the object of non ECMA standard jsapi container.
* JSAPIFastBuffer provides dynamically modified array.
* */
using ContainerError = containers::ContainerError;
using ErrorFlag = containers::ErrorFlag;
using ElementSize = base::ElementSize;
using TypedArrayHelper = base::TypedArrayHelper;
using BuiltinsArrayBuffer = builtins::BuiltinsArrayBuffer;
#define JSAPI_BUFFER_ACCESSORS(name, type) \
static JSTaggedValue Write##name(JSThread *thread, const JSHandle<JSAPIFastBuffer> &buffer, \
const JSHandle<JSTaggedValue> &value, uint32_t offset, bool littleEndian = true) \
{ \
JSType type = JSType::JS_##type##_ARRAY; \
auto byteSize = base::TypedArrayHelper::GetElementSize(type); \
JSHandle<JSTypedArray> typedArray = \
JSHandle<JSTypedArray>(thread, JSTypedArray::Cast(buffer->GetFastBufferData(thread).GetTaggedObject())); \
double valueNum = value->GetNumber(); \
double left = (type##_MIN); \
double right = (type##_MAX); \
if (valueNum > right || valueNum < left) { \
std::ostringstream oss; \
oss << std::fixed << std::setprecision(0) \
<< "The value of \"value\" is out of range. It must be >= " << left << " and <= " << right \
<< ". Received value is: "; \
if (value->IsInt()) { \
oss << std::fixed << std::setprecision(0) << valueNum; \
} else { \
oss << valueNum; \
} \
JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, oss.str().c_str()); \
THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); \
} \
if (offset + NumberSize::type > buffer->GetLength()) { \
std::ostringstream oss; \
oss << "The value of \"offset\" is out of range. It must be >= " << 0 \
<< " and <= " << static_cast<int64_t>(buffer->GetLength()) - NumberSize::type \
<< ". Received value is: " << offset; \
JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, oss.str().c_str()); \
THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); \
} \
JSAPIFastBuffer::SetValueByIndex(thread, typedArray.GetTaggedValue(), buffer->GetOffset() + offset, \
value.GetTaggedValue(), type, littleEndian); \
return JSTaggedValue(offset + byteSize); \
} \
static JSTaggedValue Read##name(JSThread *thread, const JSHandle<JSAPIFastBuffer> &buffer, uint32_t offset, \
bool littleEndian = true) \
{ \
JSType type = JSType::JS_##type##_ARRAY; \
JSHandle<JSTypedArray> typedArray = \
JSHandle<JSTypedArray>(thread, JSTypedArray::Cast(buffer->GetFastBufferData(thread).GetTaggedObject())); \
if (offset + NumberSize::type > buffer->GetLength()) { \
std::ostringstream oss; \
oss << "The value of \"offset\" is out of range. It must be >= " << 0 \
<< " and <= " << static_cast<int64_t>(buffer->GetLength()) - NumberSize::type \
<< ". Received value is: " << offset; \
JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, oss.str().c_str()); \
THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); \
} \
return JSAPIFastBuffer::GetValueByIndex(thread, typedArray.GetTaggedValue(), buffer->GetOffset() + offset, \
type, littleEndian); \
}
class JSAPIFastBuffer : public JSObject {
public:
enum EncodingType { INVALID = 0, ASCII, UTF8, UTF16LE, BASE64, BASE64URL, LATIN1, BINARY, HEX };
enum NumberSize {
UINT8 = 1,
INT8 = 1,
UINT16 = 2,
INT16 = 2,
UINT32 = 4,
INT32 = 4,
FLOAT32 = 4,
FLOAT64 = 8,
BIGINT64 = 8,
BIGUINT64 = 8
};
enum ByteLength {
OneByte = 1,
TwoBytes,
ThreeBytes,
FourBytes,
FiveBytes,
SixBytes,
SevenBytes,
EightBytes,
OneByteBits = 8,
};
static constexpr uint32_t ONE_BYTE_BIT_LENGTH = 8;
static constexpr uint32_t DEFAULT_CAPACITY_LENGTH = 4;
static constexpr uint32_t UINT8_MIN = 0;
static constexpr uint32_t UINT16_MIN = 0;
static constexpr uint32_t UINT32_MIN = 0;
static constexpr float FLOAT32_MIN = -std::numeric_limits<float>::max();
static constexpr float FLOAT32_MAX = std::numeric_limits<float>::max();
static constexpr double FLOAT64_MIN = -std::numeric_limits<double>::max();
static constexpr double FLOAT64_MAX = std::numeric_limits<double>::max();
static JSAPIFastBuffer *Cast(TaggedObject *object)
{
ASSERT(JSTaggedValue(object).IsJSAPIBuffer());
return static_cast<JSAPIFastBuffer *>(object);
}
static JSTaggedValue WriteString(JSThread *thread, JSHandle<JSAPIFastBuffer> &buffer,
JSHandle<JSTaggedValue> &value, uint32_t offset, uint32_t maxLen,
EncodingType encoding);
static JSTaggedValue WriteBytesValue(JSThread *thread, JSHandle<JSAPIFastBuffer> &buffer,
JSHandle<JSTaggedValue> &value, uint32_t offset, ByteLength byteLength,
bool littleEndian = false);
static JSTaggedValue ReadBytes(JSThread *thread, JSHandle<JSAPIFastBuffer> &buffer, uint32_t offset,
ByteLength byteLength, bool littleEndian = false);
static JSTaggedValue ReadInt(JSThread *thread, JSHandle<JSAPIFastBuffer> &buffer, uint32_t offset,
ByteLength byteLength, bool littleEndian = false);
static JSTaggedValue WriteBigUInt64(JSThread *thread, const JSHandle<JSAPIFastBuffer> &buffer,
const JSHandle<JSTaggedValue> &value, uint32_t offset,
bool littleEndian = true);
static JSTaggedValue ReadBigUInt64(JSThread *thread, const JSHandle<JSAPIFastBuffer> &buffer, uint32_t offset,
bool littleEndian = true);
static JSTaggedValue WriteBigInt64(JSThread *thread, const JSHandle<JSAPIFastBuffer> &buffer,
const JSHandle<JSTaggedValue> &value, uint32_t offset, bool littleEndian = true);
static JSTaggedValue ReadBigInt64(JSThread *thread, const JSHandle<JSAPIFastBuffer> &buffer, uint32_t offset,
bool littleEndian = true);
static JSTaggedValue Fill(JSThread *thread, JSHandle<JSAPIFastBuffer> &buffer, JSHandle<JSTaggedValue> &valueHandle,
EncodingType encoding, uint32_t start, uint32_t end);
static JSTaggedValue FillString(JSThread *thread, JSHandle<JSAPIFastBuffer> &buffer,
JSHandle<JSTaggedValue> &valueHandle, EncodingType encoding, uint32_t start,
uint32_t end);
static void ExtendBufferCapacity(JSThread *thread, JSHandle<JSAPIFastBuffer> &dst, uint32_t capacity);
static JSTaggedValue WriteStringLoop(JSThread *thread, JSHandle<JSAPIFastBuffer> &buffer, std::string_view data,
uint32_t start, uint32_t end);
static JSTaggedValue WriteBufferObjectLoop(JSThread *thread, JSHandle<JSAPIFastBuffer> &buffer,
JSHandle<JSTaggedValue> &srcObj, uint32_t start, uint32_t end);
static JSTaggedValue Copy(JSThread *thread, const JSHandle<JSTaggedValue> &dst, const JSHandle<JSTaggedValue> &src,
uint32_t tStart, uint32_t sStart, uint32_t sEnd);
static JSTaggedValue FromArrayBuffer(JSThread *thread, const JSHandle<JSAPIFastBuffer> &buffer,
const JSHandle<JSTaggedValue> &src, uint32_t byteOffset, uint32_t length);
static JSTaggedValue FromString(JSThread *thread, const JSHandle<JSAPIFastBuffer> &buffer,
const JSHandle<JSTaggedValue> &str, const JSHandle<JSTaggedValue> &encoding);
static JSTaggedValue FromString(JSThread *thread, const JSHandle<JSAPIFastBuffer> &buffer,
const JSHandle<JSTaggedValue> &str, EncodingType encoding = UTF8);
static JSTaggedValue ToString(JSThread *thread, JSHandle<JSAPIFastBuffer> &buffer, EncodingType encodingType,
uint32_t start, uint32_t end);
static std::string GetString(JSThread *thread, const JSHandle<JSTaggedValue> &str,
JSHandle<JSTaggedValue> encoding);
static std::string GetString(JSThread *thread, const JSHandle<JSTaggedValue> &str,
EncodingType encodingType = UTF8);
static std::string_view FastGetString(const JSHandle<JSTaggedValue> &str, EncodingType encodingType,
std::string &strDecoded);
static JSTaggedValue AllocateFromBufferObject(JSThread *thread, const JSHandle<JSAPIFastBuffer> &buffer,
const JSHandle<JSTaggedValue> &src, uint32_t byteLength = 0,
uint32_t byteOffset = 0);
static JSHandle<JSTypedArray> NewUint8Array(JSThread *thread, uint32_t length);
static JSTaggedValue AllocateFastBuffer(JSThread *thread, const JSHandle<JSAPIFastBuffer> &buffer,
uint32_t byteLength, uint32_t byteOffset = 0);
static JSTaggedValue FromBufferToArray(JSThread *thread, JSHandle<JSTaggedValue> &value);
static JSTaggedValue Entries(JSThread *thread, JSHandle<JSAPIFastBuffer> &buffer);
static JSTaggedValue Keys(JSThread *thread, JSHandle<JSAPIFastBuffer> &buffer);
static JSTaggedValue Values(JSThread *thread, JSHandle<JSAPIFastBuffer> &buffer);
static JSTaggedValue Includes(JSThread *thread, JSHandle<JSAPIFastBuffer> &buffer,
JSHandle<JSTaggedValue> valueHandle, uint32_t start = 0,
EncodingType encoding = UTF8);
static JSTaggedValue IndexOf(JSThread *thread, JSHandle<JSAPIFastBuffer> &buffer,
JSHandle<JSTaggedValue> valueHandle, uint32_t start = 0, EncodingType encoding = UTF8,
bool isReverse = false);
static JSTaggedValue Compare(JSThread *thread, JSHandle<JSAPIFastBuffer> &a, JSHandle<JSTaggedValue> &b,
uint32_t sStart, uint32_t sEnd, uint32_t tStart, uint32_t tEnd);
static JSTaggedValue GetArrayBuffer(JSThread *thread, JSHandle<JSAPIFastBuffer> &buffer);
static JSTaggedValue CreateBufferFromArrayLike(JSThread *thread, const JSHandle<JSAPIFastBuffer> &buffer,
const JSHandle<JSTaggedValue> &obj);
static EncodingType GetEncodingType(std::string encode);
static EncodingType GetEncodingType(JSThread *thread, const JSHandle<JSTaggedValue> &encode);
static OperationResult GetProperty(JSThread *thread, const JSHandle<JSAPIFastBuffer> &obj,
const JSHandle<JSTaggedValue> &key);
static bool SetProperty(JSThread *thread, const JSHandle<JSAPIFastBuffer> &obj, const JSHandle<JSTaggedValue> &key,
const JSHandle<JSTaggedValue> &value);
static constexpr size_t TYPEDARRAY_OFFSET = JSObject::SIZE;
ACCESSORS(FastBufferData, TYPEDARRAY_OFFSET, BUFFER_LENGTH_OFFSET)
ACCESSORS_PRIMITIVE_FIELD(Length, uint32_t, BUFFER_LENGTH_OFFSET, OFFSET_OFFSET)
ACCESSORS_PRIMITIVE_FIELD(Offset, uint32_t, OFFSET_OFFSET, LAST_OFFSET)
DEFINE_ALIGN_SIZE(LAST_OFFSET);
static const uint32_t MAX_BUFFER_INDEX = MAX_ELEMENT_INDEX;
DECL_DUMP()
DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, TYPEDARRAY_OFFSET, BUFFER_LENGTH_OFFSET)
inline uint32_t GetSize() const
{
return GetLength();
}
inline uint32_t End() const
{
return GetOffset() + GetLength();
}
inline uint32_t Begin(uint32_t offset = 0) const
{
ASSERT(GetOffset() + offset < End());
return GetOffset() + offset;
}
JSAPI_BUFFER_ACCESSORS(UInt8, UINT8);
JSAPI_BUFFER_ACCESSORS(UInt16, UINT16);
JSAPI_BUFFER_ACCESSORS(UInt32, UINT32);
JSAPI_BUFFER_ACCESSORS(Int8, INT8);
JSAPI_BUFFER_ACCESSORS(Int16, INT16);
JSAPI_BUFFER_ACCESSORS(Int32, INT32);
JSAPI_BUFFER_ACCESSORS(Float32, FLOAT32);
JSAPI_BUFFER_ACCESSORS(Float64, FLOAT64);
private:
JSTaggedValue SetValueByIndex(JSThread *thread, uint32_t index, JSTaggedValue value, JSType jsType,
bool littleEndian = true)
{
auto typedArray = this->GetFastBufferData(thread);
return SetValueByIndex(thread, typedArray, index, value, jsType, littleEndian);
}
static JSTaggedValue SetValueByIndex(JSThread *thread, const JSTaggedValue typedarray, uint32_t index,
JSTaggedValue value, JSType jsType, bool littleEndian = true);
static JSTaggedValue GetValueByIndex(JSThread *thread, const JSTaggedValue typedarray, uint32_t index,
JSType jsType, bool littleEndian = true);
static bool WriteBytes(JSThread *thread, const uint8_t *src, unsigned int size, uint8_t *dest);
static bool WriteDataLoop(const uint8_t *src, uint8_t *dest, uint32_t length, uint32_t start, uint32_t end);
static bool WriteBytes(JSThread *thread, JSHandle<JSTaggedValue> srcData, JSHandle<JSTaggedValue> dstData,
uint32_t sStart, uint32_t tStart, uint32_t size);
static int32_t StringMatch(JSThread *thread, JSHandle<JSAPIFastBuffer> &buffer, const uint8_t *str, uint32_t start,
uint32_t strLength, bool isReverse = false);
};
class StringConverter {
public:
static constexpr uint32_t LOWER_EIGHT_BITS_MASK = 0x00FF;
static constexpr uint8_t HIGER_4_BITS_MASK = 0xF0;
static constexpr uint8_t FOUR_BYTES_STYLE = 0xF0;
static constexpr uint8_t THREE_BYTES_STYLE = 0xE0;
static constexpr uint8_t TWO_BYTES_STYLE1 = 0xD0;
static constexpr uint8_t TWO_BYTES_STYLE2 = 0xC0;
static constexpr uint32_t LOWER_10_BITS_MASK = 0x03FFU;
static constexpr uint32_t LOWER_8_BITS_MASK = 0x00FFU;
static constexpr uint8_t LOWER_6_BITS_MASK = 0x3FU;
static constexpr uint8_t LOWER_5_BITS_MASK = 0x1FU;
static constexpr uint8_t LOWER_4_BITS_MASK = 0x0FU;
static constexpr uint8_t LOWER_3_BITS_MASK = 0x07U;
static constexpr uint8_t LOWER_2_BITS_MASK = 0x03U;
static constexpr uint8_t MIDDLE_4_BITS_MASK = 0x3CU;
static constexpr uint32_t HIGH_AGENT_MASK = 0xD800U;
static constexpr uint32_t LOW_AGENT_MASK = 0xDC00U;
static constexpr uint32_t UTF8_VALID_BITS = 6;
static constexpr uint32_t UTF8_ONE_BYTE_MAX = 0x007F;
static constexpr uint32_t UTF8_ONE_BYTE_SCALE = UTF8_ONE_BYTE_MAX + 1;
static constexpr uint32_t UTF8_TWO_BYTES_MAX = 0x07FF;
static constexpr uint32_t HIGH_AGENT_RANGE_FROM = 0xD800;
static constexpr uint32_t HIGH_AGENT_RANGE_TO = 0xDBFF;
static constexpr uint32_t LOW_AGENT_RANGE_FROM = 0xDC00;
static constexpr uint8_t UTF8_TWO_BYTES_HEAD_BYTE_MASK = 0xC0;
static constexpr uint8_t UTF8_TAIL_BYTE_MASK = 0x80;
static constexpr uint8_t UTF8_THREE_BYTES_HEAD_BYTE_MASK = 0xE0;
static constexpr uint8_t UTF8_FOUR_BYTES_HEAD_BYTE_MASK = 0xF0;
static constexpr uint32_t UTF16_SPECIAL_VALUE = 0x10000;
static void Utf8ToUtf16BEToData(const unsigned char *data, std::u16string &u16Str, std::string::size_type &index,
uint8_t &c1);
static std::u16string Utf8ToUtf16BE(const std::string &u8Str, bool *ok = nullptr);
static std::string Utf16BEToANSI(const std::u16string &wstr);
static std::u16string Utf16BEToLE(const std::u16string &wstr);
static std::string Utf8ToUtf16BEToANSI(const std::string &str);
static std::string Utf16StrToStr(std::u16string &value);
static void Latin1Encode(const unsigned char *data, uint32_t len, std::string &ret);
static void Base64Encode(const unsigned char *encodedStr, uint32_t len, std::string &ret, bool isUrl = false);
static void Base64Decode(std::string_view encodedStr, std::string &ret);
static void HexEncode(const unsigned char *hexStr, uint32_t len, std::string &ret);
static void HexDecode(std::string_view hexStr, std::string &ret);
};
}
#endif