1 /* 2 * Copyright (c) 2025 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16 #ifndef ECMASCRIPT_JS_API_JS_API_BUFFER_H 17 #define ECMASCRIPT_JS_API_JS_API_BUFFER_H 18 19 #include "ecmascript/base/atomic_helper.h" 20 #include "ecmascript/ecma_string.h" 21 #include "ecmascript/js_handle.h" 22 #include "ecmascript/js_object.h" 23 #include "ecmascript/containers/containers_errors.h" 24 #include "ecmascript/js_tagged_value-inl.h" 25 #include "ecmascript/js_tagged_value.h" 26 #include "ecmascript/js_typed_array.h" 27 #include "ecmascript/base/typed_array_helper-inl.h" 28 #include <iomanip> 29 #include <ios> 30 #include <string_view> 31 32 namespace panda::ecmascript { 33 /** 34 * Provide the object of non ECMA standard jsapi container. 35 * JSAPIFastBuffer provides dynamically modified array. 36 * */ 37 using ContainerError = containers::ContainerError; 38 using ErrorFlag = containers::ErrorFlag; 39 using ElementSize = base::ElementSize; 40 using TypedArrayHelper = base::TypedArrayHelper; 41 using BuiltinsArrayBuffer = builtins::BuiltinsArrayBuffer; 42 43 #define JSAPI_BUFFER_ACCESSORS(name, type) \ 44 static JSTaggedValue Write##name(JSThread *thread, const JSHandle<JSAPIFastBuffer> &buffer, \ 45 const JSHandle<JSTaggedValue> &value, uint32_t offset, bool littleEndian = true) \ 46 { \ 47 JSType type = JSType::JS_##type##_ARRAY; \ 48 auto byteSize = base::TypedArrayHelper::GetElementSize(type); \ 49 JSHandle<JSTypedArray> typedArray = \ 50 JSHandle<JSTypedArray>(thread, JSTypedArray::Cast(buffer->GetFastBufferData(thread).GetTaggedObject())); \ 51 double valueNum = value->GetNumber(); \ 52 double left = (type##_MIN); \ 53 double right = (type##_MAX); \ 54 if (valueNum > right || valueNum < left) { \ 55 std::ostringstream oss; \ 56 oss << std::fixed << std::setprecision(0) \ 57 << "The value of \"value\" is out of range. It must be >= " << left << " and <= " << right \ 58 << ". Received value is: "; \ 59 if (value->IsInt()) { \ 60 oss << std::fixed << std::setprecision(0) << valueNum; \ 61 } else { \ 62 oss << valueNum; \ 63 } \ 64 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, oss.str().c_str()); \ 65 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); \ 66 } \ 67 if (offset + NumberSize::type > buffer->GetLength()) { \ 68 std::ostringstream oss; \ 69 oss << "The value of \"offset\" is out of range. It must be >= " << 0 \ 70 << " and <= " << static_cast<int64_t>(buffer->GetLength()) - NumberSize::type \ 71 << ". Received value is: " << offset; \ 72 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, oss.str().c_str()); \ 73 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); \ 74 } \ 75 JSAPIFastBuffer::SetValueByIndex(thread, typedArray.GetTaggedValue(), buffer->GetOffset() + offset, \ 76 value.GetTaggedValue(), type, littleEndian); \ 77 return JSTaggedValue(offset + byteSize); \ 78 } \ 79 static JSTaggedValue Read##name(JSThread *thread, const JSHandle<JSAPIFastBuffer> &buffer, uint32_t offset, \ 80 bool littleEndian = true) \ 81 { \ 82 JSType type = JSType::JS_##type##_ARRAY; \ 83 JSHandle<JSTypedArray> typedArray = \ 84 JSHandle<JSTypedArray>(thread, JSTypedArray::Cast(buffer->GetFastBufferData(thread).GetTaggedObject())); \ 85 if (offset + NumberSize::type > buffer->GetLength()) { \ 86 std::ostringstream oss; \ 87 oss << "The value of \"offset\" is out of range. It must be >= " << 0 \ 88 << " and <= " << static_cast<int64_t>(buffer->GetLength()) - NumberSize::type \ 89 << ". Received value is: " << offset; \ 90 JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, oss.str().c_str()); \ 91 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); \ 92 } \ 93 return JSAPIFastBuffer::GetValueByIndex(thread, typedArray.GetTaggedValue(), buffer->GetOffset() + offset, \ 94 type, littleEndian); \ 95 } 96 97 class JSAPIFastBuffer : public JSObject { 98 public: 99 enum EncodingType { INVALID = 0, ASCII, UTF8, UTF16LE, BASE64, BASE64URL, LATIN1, BINARY, HEX }; 100 enum NumberSize { 101 UINT8 = 1, 102 INT8 = 1, 103 UINT16 = 2, 104 INT16 = 2, 105 UINT32 = 4, 106 INT32 = 4, 107 FLOAT32 = 4, 108 FLOAT64 = 8, 109 BIGINT64 = 8, 110 BIGUINT64 = 8 111 }; 112 enum ByteLength { 113 OneByte = 1, 114 TwoBytes, 115 ThreeBytes, 116 FourBytes, 117 FiveBytes, 118 SixBytes, 119 SevenBytes, 120 EightBytes, 121 OneByteBits = 8, 122 }; 123 static constexpr uint32_t ONE_BYTE_BIT_LENGTH = 8; 124 static constexpr uint32_t DEFAULT_CAPACITY_LENGTH = 4; 125 static constexpr uint32_t UINT8_MIN = 0; 126 static constexpr uint32_t UINT16_MIN = 0; 127 static constexpr uint32_t UINT32_MIN = 0; 128 static constexpr float FLOAT32_MIN = -std::numeric_limits<float>::max(); 129 static constexpr float FLOAT32_MAX = std::numeric_limits<float>::max(); 130 static constexpr double FLOAT64_MIN = -std::numeric_limits<double>::max(); 131 static constexpr double FLOAT64_MAX = std::numeric_limits<double>::max(); Cast(TaggedObject * object)132 static JSAPIFastBuffer *Cast(TaggedObject *object) 133 { 134 ASSERT(JSTaggedValue(object).IsJSAPIBuffer()); 135 return static_cast<JSAPIFastBuffer *>(object); 136 } 137 138 // these operation will modify buffer 139 static JSTaggedValue WriteString(JSThread *thread, JSHandle<JSAPIFastBuffer> &buffer, 140 JSHandle<JSTaggedValue> &value, uint32_t offset, uint32_t maxLen, 141 EncodingType encoding); 142 static JSTaggedValue WriteBytesValue(JSThread *thread, JSHandle<JSAPIFastBuffer> &buffer, 143 JSHandle<JSTaggedValue> &value, uint32_t offset, ByteLength byteLength, 144 bool littleEndian = false); 145 static JSTaggedValue ReadBytes(JSThread *thread, JSHandle<JSAPIFastBuffer> &buffer, uint32_t offset, 146 ByteLength byteLength, bool littleEndian = false); 147 static JSTaggedValue ReadInt(JSThread *thread, JSHandle<JSAPIFastBuffer> &buffer, uint32_t offset, 148 ByteLength byteLength, bool littleEndian = false); 149 150 static JSTaggedValue WriteBigUInt64(JSThread *thread, const JSHandle<JSAPIFastBuffer> &buffer, 151 const JSHandle<JSTaggedValue> &value, uint32_t offset, 152 bool littleEndian = true); 153 static JSTaggedValue ReadBigUInt64(JSThread *thread, const JSHandle<JSAPIFastBuffer> &buffer, uint32_t offset, 154 bool littleEndian = true); 155 static JSTaggedValue WriteBigInt64(JSThread *thread, const JSHandle<JSAPIFastBuffer> &buffer, 156 const JSHandle<JSTaggedValue> &value, uint32_t offset, bool littleEndian = true); 157 static JSTaggedValue ReadBigInt64(JSThread *thread, const JSHandle<JSAPIFastBuffer> &buffer, uint32_t offset, 158 bool littleEndian = true); 159 static JSTaggedValue Fill(JSThread *thread, JSHandle<JSAPIFastBuffer> &buffer, JSHandle<JSTaggedValue> &valueHandle, 160 EncodingType encoding, uint32_t start, uint32_t end); 161 static JSTaggedValue FillString(JSThread *thread, JSHandle<JSAPIFastBuffer> &buffer, 162 JSHandle<JSTaggedValue> &valueHandle, EncodingType encoding, uint32_t start, 163 uint32_t end); 164 165 // query or create buffer 166 static void ExtendBufferCapacity(JSThread *thread, JSHandle<JSAPIFastBuffer> &dst, uint32_t capacity); 167 static JSTaggedValue WriteStringLoop(JSThread *thread, JSHandle<JSAPIFastBuffer> &buffer, std::string_view data, 168 uint32_t start, uint32_t end); 169 static JSTaggedValue WriteBufferObjectLoop(JSThread *thread, JSHandle<JSAPIFastBuffer> &buffer, 170 JSHandle<JSTaggedValue> &srcObj, uint32_t start, uint32_t end); 171 static JSTaggedValue Copy(JSThread *thread, const JSHandle<JSTaggedValue> &dst, const JSHandle<JSTaggedValue> &src, 172 uint32_t tStart, uint32_t sStart, uint32_t sEnd); 173 static JSTaggedValue FromArrayBuffer(JSThread *thread, const JSHandle<JSAPIFastBuffer> &buffer, 174 const JSHandle<JSTaggedValue> &src, uint32_t byteOffset, uint32_t length); 175 static JSTaggedValue FromString(JSThread *thread, const JSHandle<JSAPIFastBuffer> &buffer, 176 const JSHandle<JSTaggedValue> &str, const JSHandle<JSTaggedValue> &encoding); 177 static JSTaggedValue FromString(JSThread *thread, const JSHandle<JSAPIFastBuffer> &buffer, 178 const JSHandle<JSTaggedValue> &str, EncodingType encoding = UTF8); 179 static JSTaggedValue ToString(JSThread *thread, JSHandle<JSAPIFastBuffer> &buffer, EncodingType encodingType, 180 uint32_t start, uint32_t end); 181 static std::string GetString(JSThread *thread, const JSHandle<JSTaggedValue> &str, 182 JSHandle<JSTaggedValue> encoding); 183 static std::string GetString(JSThread *thread, const JSHandle<JSTaggedValue> &str, 184 EncodingType encodingType = UTF8); 185 static std::string_view FastGetString(const JSHandle<JSTaggedValue> &str, EncodingType encodingType, 186 std::string &strDecoded); 187 static JSTaggedValue AllocateFromBufferObject(JSThread *thread, const JSHandle<JSAPIFastBuffer> &buffer, 188 const JSHandle<JSTaggedValue> &src, uint32_t byteLength = 0, 189 uint32_t byteOffset = 0); 190 static JSHandle<JSTypedArray> NewUint8Array(JSThread *thread, uint32_t length); 191 static JSTaggedValue AllocateFastBuffer(JSThread *thread, const JSHandle<JSAPIFastBuffer> &buffer, 192 uint32_t byteLength, uint32_t byteOffset = 0); 193 static JSTaggedValue FromBufferToArray(JSThread *thread, JSHandle<JSTaggedValue> &value); 194 static JSTaggedValue Entries(JSThread *thread, JSHandle<JSAPIFastBuffer> &buffer); 195 static JSTaggedValue Keys(JSThread *thread, JSHandle<JSAPIFastBuffer> &buffer); 196 static JSTaggedValue Values(JSThread *thread, JSHandle<JSAPIFastBuffer> &buffer); 197 static JSTaggedValue Includes(JSThread *thread, JSHandle<JSAPIFastBuffer> &buffer, 198 JSHandle<JSTaggedValue> valueHandle, uint32_t start = 0, 199 EncodingType encoding = UTF8); 200 static JSTaggedValue IndexOf(JSThread *thread, JSHandle<JSAPIFastBuffer> &buffer, 201 JSHandle<JSTaggedValue> valueHandle, uint32_t start = 0, EncodingType encoding = UTF8, 202 bool isReverse = false); 203 static JSTaggedValue Compare(JSThread *thread, JSHandle<JSAPIFastBuffer> &a, JSHandle<JSTaggedValue> &b, 204 uint32_t sStart, uint32_t sEnd, uint32_t tStart, uint32_t tEnd); 205 static JSTaggedValue GetArrayBuffer(JSThread *thread, JSHandle<JSAPIFastBuffer> &buffer); 206 static JSTaggedValue CreateBufferFromArrayLike(JSThread *thread, const JSHandle<JSAPIFastBuffer> &buffer, 207 const JSHandle<JSTaggedValue> &obj); 208 static EncodingType GetEncodingType(std::string encode); 209 static EncodingType GetEncodingType(JSThread *thread, const JSHandle<JSTaggedValue> &encode); 210 static OperationResult GetProperty(JSThread *thread, const JSHandle<JSAPIFastBuffer> &obj, 211 const JSHandle<JSTaggedValue> &key); 212 static bool SetProperty(JSThread *thread, const JSHandle<JSAPIFastBuffer> &obj, const JSHandle<JSTaggedValue> &key, 213 const JSHandle<JSTaggedValue> &value); 214 215 static constexpr size_t TYPEDARRAY_OFFSET = JSObject::SIZE; 216 ACCESSORS(FastBufferData, TYPEDARRAY_OFFSET, BUFFER_LENGTH_OFFSET) 217 ACCESSORS_PRIMITIVE_FIELD(Length, uint32_t, BUFFER_LENGTH_OFFSET, OFFSET_OFFSET) 218 ACCESSORS_PRIMITIVE_FIELD(Offset, uint32_t, OFFSET_OFFSET, LAST_OFFSET) 219 DEFINE_ALIGN_SIZE(LAST_OFFSET); 220 static const uint32_t MAX_BUFFER_INDEX = MAX_ELEMENT_INDEX; 221 DECL_DUMP() DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject,TYPEDARRAY_OFFSET,BUFFER_LENGTH_OFFSET)222 DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, TYPEDARRAY_OFFSET, BUFFER_LENGTH_OFFSET) 223 224 inline uint32_t GetSize() const 225 { 226 return GetLength(); 227 } 228 End()229 inline uint32_t End() const 230 { 231 return GetOffset() + GetLength(); 232 } 233 234 inline uint32_t Begin(uint32_t offset = 0) const 235 { 236 ASSERT(GetOffset() + offset < End()); 237 return GetOffset() + offset; 238 } 239 240 JSAPI_BUFFER_ACCESSORS(UInt8, UINT8); 241 JSAPI_BUFFER_ACCESSORS(UInt16, UINT16); 242 JSAPI_BUFFER_ACCESSORS(UInt32, UINT32); 243 JSAPI_BUFFER_ACCESSORS(Int8, INT8); 244 JSAPI_BUFFER_ACCESSORS(Int16, INT16); 245 JSAPI_BUFFER_ACCESSORS(Int32, INT32); 246 JSAPI_BUFFER_ACCESSORS(Float32, FLOAT32); 247 JSAPI_BUFFER_ACCESSORS(Float64, FLOAT64); 248 249 private: 250 JSTaggedValue SetValueByIndex(JSThread *thread, uint32_t index, JSTaggedValue value, JSType jsType, 251 bool littleEndian = true) 252 { 253 auto typeArray = this->GetFastBufferData(thread); 254 return SetValueByIndex(thread, typeArray, index, value, jsType, littleEndian); 255 } 256 static JSTaggedValue SetValueByIndex(JSThread *thread, const JSTaggedValue typedarray, uint32_t index, 257 JSTaggedValue value, JSType jsType, bool littleEndian = true); 258 static JSTaggedValue GetValueByIndex(JSThread *thread, const JSTaggedValue typedarray, uint32_t index, 259 JSType jsType, bool littleEndian = true); 260 static bool WriteBytes(JSThread *thread, const uint8_t *src, unsigned int size, uint8_t *dest); 261 static bool WriteDataLoop(const uint8_t *src, uint8_t *dest, uint32_t length, uint32_t start, uint32_t end); 262 static bool WriteBytes(JSThread *thread, JSHandle<JSTaggedValue> srcData, JSHandle<JSTaggedValue> dstData, 263 uint32_t sStart, uint32_t tStart, uint32_t size); 264 static int32_t StringMatch(JSThread *thread, JSHandle<JSAPIFastBuffer> &buffer, const uint8_t *str, uint32_t start, 265 uint32_t strLength, bool isReverse = false); 266 }; 267 268 class StringConverter { 269 public: 270 static constexpr uint32_t LOWER_EIGHT_BITS_MASK = 0x00FF; 271 static constexpr uint8_t HIGER_4_BITS_MASK = 0xF0; 272 static constexpr uint8_t FOUR_BYTES_STYLE = 0xF0; 273 static constexpr uint8_t THREE_BYTES_STYLE = 0xE0; 274 static constexpr uint8_t TWO_BYTES_STYLE1 = 0xD0; 275 static constexpr uint8_t TWO_BYTES_STYLE2 = 0xC0; 276 static constexpr uint32_t LOWER_10_BITS_MASK = 0x03FFU; 277 static constexpr uint32_t LOWER_8_BITS_MASK = 0x00FFU; 278 static constexpr uint8_t LOWER_6_BITS_MASK = 0x3FU; 279 static constexpr uint8_t LOWER_5_BITS_MASK = 0x1FU; 280 static constexpr uint8_t LOWER_4_BITS_MASK = 0x0FU; 281 static constexpr uint8_t LOWER_3_BITS_MASK = 0x07U; 282 static constexpr uint8_t LOWER_2_BITS_MASK = 0x03U; 283 static constexpr uint8_t MIDDLE_4_BITS_MASK = 0x3CU; 284 static constexpr uint32_t HIGH_AGENT_MASK = 0xD800U; 285 static constexpr uint32_t LOW_AGENT_MASK = 0xDC00U; 286 static constexpr uint32_t UTF8_VALID_BITS = 6; 287 static constexpr uint32_t UTF8_ONE_BYTE_MAX = 0x007F; 288 static constexpr uint32_t UTF8_ONE_BYTE_SCALE = UTF8_ONE_BYTE_MAX + 1; 289 static constexpr uint32_t UTF8_TWO_BYTES_MAX = 0x07FF; 290 static constexpr uint32_t HIGH_AGENT_RANGE_FROM = 0xD800; 291 static constexpr uint32_t HIGH_AGENT_RANGE_TO = 0xDBFF; 292 static constexpr uint32_t LOW_AGENT_RANGE_FROM = 0xDC00; 293 static constexpr uint8_t UTF8_TWO_BYTES_HEAD_BYTE_MASK = 0xC0; 294 static constexpr uint8_t UTF8_TAIL_BYTE_MASK = 0x80; 295 static constexpr uint8_t UTF8_THREE_BYTES_HEAD_BYTE_MASK = 0xE0; 296 static constexpr uint8_t UTF8_FOUR_BYTES_HEAD_BYTE_MASK = 0xF0; 297 static constexpr uint32_t UTF16_SPECIAL_VALUE = 0x10000; 298 static void Utf8ToUtf16BEToData(const unsigned char *data, std::u16string &u16Str, std::string::size_type &index, 299 uint8_t &c1); 300 static std::u16string Utf8ToUtf16BE(const std::string &u8Str, bool *ok = nullptr); 301 static std::string Utf16BEToANSI(const std::u16string &wstr); 302 static std::u16string Utf16BEToLE(const std::u16string &wstr); 303 static std::string Utf8ToUtf16BEToANSI(const std::string &str); 304 static std::string Utf16StrToStr(std::u16string &value); 305 static void Latin1Encode(const unsigned char *data, uint32_t len, std::string &ret); 306 static void Base64Encode(const unsigned char *encodedStr, uint32_t len, std::string &ret, bool isUrl = false); 307 static void Base64Decode(std::string_view encodedStr, std::string &ret); 308 static void HexEncode(const unsigned char *hexStr, uint32_t len, std::string &ret); 309 static void HexDecode(std::string_view hexStr, std::string &ret); 310 }; 311 312 } // namespace panda::ecmascript 313 #endif // ECMASCRIPT_JS_API_JS_API_BUFFER_H 314