• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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