/* * 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/js_api/js_api_buffer.h" #include #include #include #include #include "ecmascript/base/typed_array_helper-inl.h" #include "ecmascript/base/typed_array_helper.h" #include "ecmascript/byte_array.h" #include "ecmascript/ecma_string-inl.h" #include "ecmascript/ecma_string.h" #include "ecmascript/ecma_vm.h" #include "ecmascript/js_array.h" #include "ecmascript/js_arraybuffer.h" #include "ecmascript/js_dataview.h" #include "ecmascript/js_handle.h" #include "ecmascript/js_hclass.h" #include "ecmascript/js_object.h" #include "ecmascript/js_tagged_number.h" #include "ecmascript/js_tagged_value.h" #include "ecmascript/js_typed_array.h" #include "ecmascript/object_factory.h" #include "jsnapi_expo.h" #include "macros.h" namespace panda::ecmascript { using ContainerError = containers::ContainerError; using string = std::string; using string_view = std::string_view; using ErrorFlag = containers::ErrorFlag; using ElementSize = base::ElementSize; using EncodingType = JSAPIFastBuffer::EncodingType; using TypedArrayHelper = base::TypedArrayHelper; using BuiltinsArrayBuffer = builtins::BuiltinsArrayBuffer; using u16string = std::u16string; namespace { const std::map ENCODING_MAP = { {"hex", EncodingType::HEX}, {"base64url", EncodingType::BASE64URL}, {"base64", EncodingType::BASE64}, {"ascii", EncodingType::ASCII}, {"latin1", EncodingType::LATIN1}, {"binary", EncodingType::BINARY}, {"ucs2", EncodingType::UTF16LE}, {"ucs-2", EncodingType::UTF16LE}, {"utf-16le", EncodingType::UTF16LE}, {"utf16le", EncodingType::UTF16LE}, {"utf-8", EncodingType::UTF8}, {"utf8", EncodingType::UTF8}, }; } // namespace const uint8_t BASE64_TABLE[256] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -2, -1, -1, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, 62, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, 63, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; inline bool RangeChecker(uint32_t rangeLeft, uint32_t rangeRight, uint32_t l, uint32_t r) { return l <= r && l <= rangeRight && r >= rangeLeft; } bool IsBase64Char(unsigned char c) { return (isalnum(c) || (c == '+') || (c == '/') || (c == '-') || (c == '_')); } EncodingType JSAPIFastBuffer::GetEncodingType(string encode) { if (ENCODING_MAP.find(encode) != ENCODING_MAP.end()) { return ENCODING_MAP.at(encode); } return EncodingType::INVALID; } EncodingType JSAPIFastBuffer::GetEncodingType(JSThread *thread, const JSHandle &encode) { auto strAccessor = EcmaStringAccessor(JSHandle(encode)); auto str = strAccessor.ToStdString(thread); return GetEncodingType(str); } 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 buffer) { return GetUInt8ArrayFromBufferObject(thread, buffer.GetTaggedValue()); } uint8_t *GetUnderlyingData(JSThread *thread, JSTypedArray *array, uint32_t offset) { if (array->GetByteLength() <= offset) { return nullptr; } JSTaggedValue arrayBuffer = array->GetViewedArrayBufferOrByteArray(thread); if (BuiltinsArrayBuffer::IsDetachedBuffer(thread, arrayBuffer)) { return nullptr; } auto res = reinterpret_cast(builtins::BuiltinsArrayBuffer::GetDataPointFromBuffer(thread, arrayBuffer, offset)); return res; } uint8_t *GetUnderlyingData(JSThread *thread, JSTaggedValue obj, uint32_t offset = 0) { if (obj.IsJSAPIBuffer()) { JSAPIFastBuffer *buffer = JSAPIFastBuffer::Cast(obj.GetTaggedObject()); if (buffer->GetLength() <= offset) { return nullptr; } offset += buffer->GetOffset(); } JSTypedArray *array = GetUInt8ArrayFromBufferObject(thread, obj); return GetUnderlyingData(thread, array, offset); } int32_t GetValueInt32(JSHandle valueHandle) { JSTaggedValue value = valueHandle.GetTaggedValue(); if (UNLIKELY(value.IsDouble())) { return static_cast( ecmascript::base::NumberHelper::DoubleToInt(value.GetDouble(), ecmascript::base::INT32_BITS)); } return value.GetInt(); } uint32_t GetValueUInt32(JSHandle valueHandle) { return static_cast(GetValueInt32(valueHandle)); } string FromStringUtf8(JSThread *thread, const JSHandle &str) { auto strAccessor = EcmaStringAccessor(JSHandle(str)); auto res = strAccessor.ToStdString(thread); return res; } string_view FromStringUtf16(JSThread *thread, const JSHandle &str, string &stringDecoded) { auto strAccessor = EcmaStringAccessor(JSHandle(str)); auto u16string = StringConverter::Utf8ToUtf16BE(strAccessor.ToStdString(thread)); stringDecoded = StringConverter::Utf16StrToStr(u16string); return string_view(stringDecoded); } string_view FromStringASCII(JSThread *thread, const JSHandle &str, string &stringDecoded) { auto strAccessor = EcmaStringAccessor(JSHandle(str)); uint32_t length = strAccessor.GetLength(); stringDecoded.reserve(length); stringDecoded.resize(length); strAccessor.WriteToOneByte(thread, reinterpret_cast(stringDecoded.data()), length); return string_view(stringDecoded); } string_view FromStringBase64(JSThread *thread, const JSHandle &str, string &stringDecoded) { auto strAccessor = EcmaStringAccessor(JSHandle(str)); CVector buf; Span sp = strAccessor.ToUtf8Span(thread, buf); StringConverter::Base64Decode(string_view(reinterpret_cast(sp.data()), sp.size()), stringDecoded); return std::string_view(stringDecoded); } std::string ConvertEcmaStringToStdString(JSThread *thread, const JSHandle &str) { ASSERT(str->IsString()); auto strAccessor = EcmaStringAccessor(JSHandle(str)); return strAccessor.ToStdString(thread); } JSHandle JSAPIFastBuffer::NewUint8Array(JSThread *thread, uint32_t length) { JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); JSHandle handleTagValFunc = env->GetUint8ArrayFunction(); ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle obj = factory->NewJSObjectByConstructor(JSHandle(handleTagValFunc), handleTagValFunc); DataViewType arrayType = DataViewType::UINT8; JSTypedArray::Cast(*obj)->SetTypedArrayName(thread, thread->GlobalConstants()->GetUint8ArrayString()); TypedArrayHelper::AllocateTypedArrayBuffer(thread, obj, length, arrayType); return JSHandle(obj); } JSTaggedValue JSAPIFastBuffer::Copy(JSThread *thread, const JSHandle &dst, const JSHandle &src, uint32_t tStart, uint32_t sStart, uint32_t sEnd) { ASSERT(tStart >= 0 && sStart >= 0 && sStart <= sEnd); uint32_t copyLength = sEnd - sStart; JSTypedArray *dstArray = GetUInt8ArrayFromBufferObject(thread, dst); uint32_t dstLength = dst->IsJSAPIBuffer() ? JSHandle(dst)->GetLength() : dstArray->GetByteLength(); if (dstLength <= tStart) { return JSTaggedValue(0); } copyLength = std::min(copyLength, dstLength - tStart); uint8_t *srcData = GetUnderlyingData(thread, src.GetTaggedValue(), sStart); uint8_t *dstData = GetUnderlyingData(thread, dst.GetTaggedValue(), tStart); if (srcData == nullptr || dstData == nullptr) { JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::ARRAY_BUFFER_IS_NULL_OR_DETACHED, "The underlying ArrayBuffer is null or detached."); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } WriteBytes(thread, srcData, copyLength, dstData); return JSTaggedValue(copyLength); } JSTaggedValue JSAPIFastBuffer::CreateBufferFromArrayLike(JSThread *thread, const JSHandle &buffer, const JSHandle &obj) { if (!obj->IsECMAObject()) { JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, "CreateBufferFromArrayLike must accept object."); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } JSHandle lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString(); JSHandle value = JSObject::GetProperty(thread, obj, lengthKeyHandle).GetValue(); JSTaggedNumber number = JSTaggedValue::ToLength(thread, value); if (number.GetNumber() > JSObject::MAX_ELEMENT_INDEX) { JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, "len is bigger than 2^32 - 1."); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } uint32_t len = number.ToUint32(); auto fastBufferData = NewUint8Array(thread, len); buffer->SetFastBufferData(thread, fastBufferData); buffer->SetLength(len); for (uint32_t i = 0; i < len; i++) { JSTaggedValue next = JSTaggedValue::GetProperty(thread, obj, i).GetValue().GetTaggedValue(); if (!next.IsInt()) { JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, "value in arraylike must be a integer."); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } buffer->SetValueByIndex(thread, i, JSTaggedValue(static_cast(next.GetInt())), JSType::JS_UINT8_ARRAY); } return buffer.GetTaggedValue(); } JSTaggedValue JSAPIFastBuffer::FromArrayBuffer(JSThread *thread, const JSHandle &buffer, const JSHandle &src, uint32_t byteOffset, uint32_t length) { if (BuiltinsArrayBuffer::IsDetachedBuffer(thread, src.GetTaggedValue())) { JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::ARRAY_BUFFER_IS_NULL_OR_DETACHED, "The underlying ArrayBuffer is null or detached."); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } auto srcBuffer = JSHandle(src); uint32_t srcLength = srcBuffer->GetArrayBufferByteLength(); if (srcLength <= byteOffset) { JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, "byteOffset must less than length."); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } auto len = std::min(length, srcLength); auto array = NewUint8Array(thread, len); array->SetViewedArrayBufferOrByteArray(thread, srcBuffer); array->SetByteLength(len); array->SetArrayLength(len); buffer->SetFastBufferData(thread, array); buffer->SetLength(len); buffer->SetOffset(byteOffset); return buffer.GetTaggedValue(); } string_view FromStringHex(JSThread *thread, const JSHandle &str, string &stringDecoded) { auto strAccessor = EcmaStringAccessor(JSHandle(str)); auto hexStr = strAccessor.ToStdString(thread); StringConverter::HexDecode(hexStr, stringDecoded); return std::string_view(stringDecoded); } bool IsOneByte(uint8_t u8Char) { return (u8Char & 0x80) == 0; } void StringConverter::Utf8ToUtf16BEToData(const unsigned char *data, u16string &u16Str, string::size_type &index, uint8_t &c1) { uint8_t c2 = data[++index]; // The second byte uint8_t c3 = data[++index]; // The third byte uint8_t c4 = data[++index]; // The forth byte // Calculate the UNICODE code point value (3 bits lower for the first byte, 6 // bits for the other) 3 : shift left 3 times of UTF8_VALID_BITS uint32_t codePoint = ((c1 & LOWER_3_BITS_MASK) << (3 * UTF8_VALID_BITS)) | // 2 : shift left 2 times of UTF8_VALID_BITS ((c2 & LOWER_6_BITS_MASK) << (2 * UTF8_VALID_BITS)) | ((c3 & LOWER_6_BITS_MASK) << UTF8_VALID_BITS) | (c4 & LOWER_6_BITS_MASK); // In UTF-16, U+10000 to U+10FFFF represent surrogate pairs with two 16-bit // units if (codePoint >= UTF16_SPECIAL_VALUE) { codePoint -= UTF16_SPECIAL_VALUE; // 10 : a half of 20 , shift right 10 bits u16Str.push_back(static_cast((codePoint >> 10) | HIGH_AGENT_MASK)); u16Str.push_back(static_cast((codePoint & LOWER_10_BITS_MASK) | LOW_AGENT_MASK)); } else { // In UTF-16, U+0000 to U+D7FF and U+E000 to U+FFFF are Unicode code // point values // assume it does not exist (if any, not encoded) u16Str.push_back(static_cast(codePoint)); } } u16string StringConverter::Utf8ToUtf16BE(const string &u8Str, bool *ok) { u16string u16Str = u""; u16Str.reserve(u8Str.size()); string::size_type len = u8Str.length(); const unsigned char *data = reinterpret_cast(u8Str.data()); bool isOk = true; for (string::size_type i = 0; i < len; ++i) { uint8_t c1 = data[i]; // The first byte if (IsOneByte(c1)) { // only 1 byte represents the UNICODE code point u16Str.push_back(static_cast(c1)); continue; } switch (c1 & HIGER_4_BITS_MASK) { case FOUR_BYTES_STYLE: { // 4 byte characters, from 0x10000 to 0x10FFFF Utf8ToUtf16BEToData(data, u16Str, i, c1); break; } case THREE_BYTES_STYLE: { // 3 byte characters, from 0x800 to 0xFFFF uint8_t c2 = data[++i]; // The second byte uint8_t c3 = data[++i]; // The third byte // Calculates the UNICODE code point value // (4 bits lower for the first byte, 6 bits lower for the other) // 2 : shift left 2 times of UTF8_VALID_BITS uint32_t codePoint = ((c1 & LOWER_4_BITS_MASK) << (2 * UTF8_VALID_BITS)) | ((c2 & LOWER_6_BITS_MASK) << UTF8_VALID_BITS) | (c3 & LOWER_6_BITS_MASK); u16Str.push_back(static_cast(codePoint)); break; } case TWO_BYTES_STYLE1: // 2 byte characters, from 0x80 to 0x7FF case TWO_BYTES_STYLE2: { uint8_t c2 = data[++i]; // The second byte // Calculates the UNICODE code point value // (5 bits lower for the first byte, 6 bits lower for the other) uint32_t codePoint = ((c1 & LOWER_5_BITS_MASK) << UTF8_VALID_BITS) | (c2 & LOWER_6_BITS_MASK); u16Str.push_back(static_cast(codePoint)); break; } default: { isOk = false; break; } } } if (ok != nullptr) { *ok = isOk; } return u16Str; } u16string StringConverter::Utf16BEToLE(const u16string &wstr) { u16string str16 = u""; const char16_t *data = wstr.data(); for (unsigned int i = 0; i < wstr.length(); i++) { char16_t wc = data[i]; char16_t high = (wc >> 8) & 0x00FF; char16_t low = wc & 0x00FF; char16_t c16 = (low << 8) | high; str16.push_back(c16); } return str16; } string StringConverter::Utf16BEToANSI(const u16string &wstr) { string ret = ""; for (u16string::const_iterator it = wstr.begin(); it != wstr.end(); ++it) { char16_t wc = (*it); // get the lower bit from the UNICODE code point char c = static_cast(wc & LOWER_8_BITS_MASK); ret.push_back(c); } return ret; } string StringConverter::Utf8ToUtf16BEToANSI(const string &str) { u16string u16Str = Utf8ToUtf16BE(str); string ret = Utf16BEToANSI(u16Str); return ret; } string JSAPIFastBuffer::GetString(JSThread *thread, const JSHandle &str, EncodingType encodingType) { string_view data; string strDecoded; switch (encodingType) { case UTF8: strDecoded = FromStringUtf8(thread, str); data = strDecoded; break; case ASCII: case LATIN1: case BINARY: data = FromStringASCII(thread, str, strDecoded); break; case UTF16LE: data = FromStringUtf16(thread, str, strDecoded); break; case BASE64: case BASE64URL: data = FromStringBase64(thread, str, strDecoded); break; case HEX: data = FromStringHex(thread, str, strDecoded); break; default: return ""; } return strDecoded; } string JSAPIFastBuffer::GetString(JSThread *thread, const JSHandle &str, JSHandle encoding) { EncodingType encodingType = UTF8; encodingType = GetEncodingType(ConvertEcmaStringToStdString(thread, encoding)); return GetString(thread, str, encodingType); } JSTaggedValue JSAPIFastBuffer::FromString(JSThread *thread, const JSHandle &buffer, const JSHandle &str, const JSHandle &encoding) { if (encoding->IsUndefined() || encoding->IsNull()) { return FromString(thread, buffer, str); } EncodingType encodingType = GetEncodingType(thread, encoding); return FromString(thread, buffer, str, encodingType); } JSTaggedValue JSAPIFastBuffer::FromString(JSThread *thread, const JSHandle &buffer, const JSHandle &str, EncodingType encoding) { string_view data; string strDecoded; switch (encoding) { case UTF8: strDecoded = FromStringUtf8(thread, str); data = strDecoded; break; case ASCII: case LATIN1: case BINARY: data = FromStringASCII(thread, str, strDecoded); break; case UTF16LE: data = FromStringUtf16(thread, str, strDecoded); break; case BASE64: case BASE64URL: data = FromStringBase64(thread, str, strDecoded); break; case HEX: data = FromStringHex(thread, str, strDecoded); break; default: THROW_RANGE_ERROR_AND_RETURN(thread, "Not supported encodingType", JSTaggedValue::Exception()); } // using data to new buffer uint32_t length = data.length(); JSHandle array = NewUint8Array(thread, length); buffer->SetFastBufferData(thread, array); buffer->SetLength(length); if (length != 0) { auto *dst = GetUnderlyingData(thread, buffer.GetTaggedValue()); if (dst == nullptr) { JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::ARRAY_BUFFER_IS_NULL_OR_DETACHED, "The underlying ArrayBuffer is null or detached."); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } WriteBytes(thread, reinterpret_cast(data.data()), length, reinterpret_cast(dst)); } return buffer.GetTaggedValue(); } JSTaggedValue JSAPIFastBuffer::ToString(JSThread *thread, JSHandle &buffer, EncodingType encodingType, uint32_t start, uint32_t end) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); if (!RangeChecker(0, buffer->GetLength(), start, end)) { std::ostringstream oss; oss << "Buffer ToString parameter are out of range"; JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, oss.str().c_str()); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } auto arrayBuffer = GetArrayBuffer(thread, buffer); if (BuiltinsArrayBuffer::IsDetachedBuffer(thread, arrayBuffer)) { JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::ARRAY_BUFFER_IS_NULL_OR_DETACHED, "The underlying ArrayBuffer is null or detached."); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } uint8_t *data = reinterpret_cast( builtins::BuiltinsArrayBuffer::GetDataPointFromBuffer(thread, arrayBuffer, buffer->GetOffset() + start)); uint32_t len = end - start; string strDecoded; switch (encodingType) { case UTF8: return factory->NewFromUtf8WithoutStringTable(string_view(reinterpret_cast(data), len)) .GetTaggedValue(); case ASCII: return factory->NewFromASCII(string_view(reinterpret_cast(data), len)).GetTaggedValue(); case UTF16LE: // 2 : uint16_t is double of uint8_t return factory->NewFromUtf16(reinterpret_cast(data), len / 2).GetTaggedValue(); case LATIN1: case BINARY: StringConverter::Latin1Encode(reinterpret_cast(data), len, strDecoded); break; case BASE64: case BASE64URL: StringConverter::Base64Encode(reinterpret_cast(data), len, strDecoded, encodingType == BASE64URL); break; case HEX: StringConverter::HexEncode(reinterpret_cast(data), len, strDecoded); break; default: THROW_RANGE_ERROR_AND_RETURN(thread, "Not supported encodingType", JSTaggedValue::Exception()); } return factory->NewFromUtf8(strDecoded).GetTaggedValue(); } JSTaggedValue JSAPIFastBuffer::WriteString(JSThread *thread, JSHandle &buffer, JSHandle &value, uint32_t offset, uint32_t maxLen, EncodingType encoding) { ASSERT(value->IsString()); uint32_t bufLength = buffer->GetLength(); if (offset > bufLength) { return JSTaggedValue(0); } maxLen = std::min(maxLen, bufLength - offset); string_view data; string strDecoded; // data = string_view(strDecoded) switch (encoding) { case UTF8: strDecoded = FromStringUtf8(thread, value); data = strDecoded; break; case ASCII: case LATIN1: case BINARY: data = FromStringASCII(thread, value, strDecoded); break; case UTF16LE: strDecoded = FromStringUtf16(thread, value, strDecoded); data = strDecoded; break; case BASE64: case BASE64URL: data = FromStringBase64(thread, value, strDecoded); break; case HEX: data = FromStringHex(thread, value, strDecoded); break; default: THROW_RANGE_ERROR_AND_RETURN(thread, "Not supported encodingType", JSTaggedValue::Exception()); } uint32_t strLength = data.length(); if (strLength == 0) { return JSTaggedValue(0); } maxLen = std::min(maxLen, strLength); auto *dst = GetUnderlyingData(thread, buffer.GetTaggedValue(), offset); if (dst == nullptr) { JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::ARRAY_BUFFER_IS_NULL_OR_DETACHED, "The underlying ArrayBuffer is null or detached."); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } WriteBytes(thread, reinterpret_cast(data.data()), maxLen, reinterpret_cast(dst)); return JSTaggedValue(maxLen); } JSTaggedValue JSAPIFastBuffer::AllocateFastBuffer(JSThread *thread, const JSHandle &buffer, uint32_t byteLength, uint32_t byteOffset) { JSHandle handleUint8Array = JSAPIFastBuffer::NewUint8Array(thread, byteLength); buffer->SetFastBufferData(thread, handleUint8Array); buffer->SetLength(byteLength); buffer->SetOffset(byteOffset); return buffer.GetTaggedValue(); } JSTaggedValue JSAPIFastBuffer::AllocateFromBufferObject(JSThread *thread, const JSHandle &buffer, const JSHandle &src, uint32_t byteLength, uint32_t byteOffset) { auto handleUint8Array = JSTaggedValue(GetUInt8ArrayFromBufferObject(thread, src)); buffer->SetFastBufferData(thread, handleUint8Array); buffer->SetLength(byteLength); buffer->SetOffset(byteOffset); return buffer.GetTaggedValue(); } JSTaggedValue JSAPIFastBuffer::GetArrayBuffer(JSThread *thread, JSHandle &buffer) { // if buffer size smaller than Buffer.poolSize, it may return the pool arraybuffer. auto array = JSHandle(thread, JSTypedArray::Cast(buffer->GetFastBufferData(thread).GetTaggedObject())); return JSTypedArray::GetOffHeapBuffer(thread, array); } // return a JSArray copied from buffer.Uint8Array JSTaggedValue JSAPIFastBuffer::FromBufferToArray(JSThread *thread, JSHandle &value) { auto buffer = JSHandle(value); int32_t length = static_cast(buffer->GetLength()); JSHandle array = JSArray::ArrayCreate(thread, JSTaggedNumber(length)); JSHandle typedArray = JSHandle(thread, JSTypedArray::Cast(buffer->GetFastBufferData(thread).GetTaggedObject())); for (int i = 0; i < length; i++) { JSTaggedValue bufferValue = GetValueByIndex(thread, typedArray.GetTaggedValue(), i, JSType::JS_UINT8_ARRAY); JSArray::FastSetPropertyByValue(thread, array, i, JSHandle(thread, bufferValue)); } return array.GetTaggedValue(); } JSTaggedValue JSAPIFastBuffer::Entries(JSThread *thread, JSHandle &buffer) { auto array = JSHandle(thread, JSTypedArray::Cast(buffer->GetFastBufferData(thread).GetTaggedObject())); ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle iter( factory->NewJSArrayIterator(JSHandle(thread, array.GetTaggedValue()), IterationKind::KEY_AND_VALUE)); return iter.GetTaggedValue(); } JSTaggedValue JSAPIFastBuffer::Keys(JSThread *thread, JSHandle &buffer) { auto array = JSHandle(thread, JSTypedArray::Cast(buffer->GetFastBufferData(thread).GetTaggedObject())); ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle iter( factory->NewJSArrayIterator(JSHandle(thread, array.GetTaggedValue()), IterationKind::KEY)); return iter.GetTaggedValue(); } JSTaggedValue JSAPIFastBuffer::Values(JSThread *thread, JSHandle &buffer) { auto array = JSHandle(thread, JSTypedArray::Cast(buffer->GetFastBufferData(thread).GetTaggedObject())); ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle iter( factory->NewJSArrayIterator(JSHandle(thread, array.GetTaggedValue()), IterationKind::VALUE)); return iter.GetTaggedValue(); } std::string StringConverter::Utf16StrToStr(std::u16string &value) { string str = ""; const char16_t *data = reinterpret_cast(value.data()); for (unsigned int i = 0; i < value.length(); i++) { char16_t c = data[i]; char high = static_cast((c >> 8) & 0x00FF); char low = static_cast(c & 0x00FF); str.push_back(low); str.push_back(high); } return str; } JSTaggedValue JSAPIFastBuffer::FillString(JSThread *thread, JSHandle &buffer, JSHandle &valueHandle, EncodingType encoding, uint32_t start, uint32_t end) { string str = GetString(thread, valueHandle, encoding); if (str.length() == 0) { str.push_back(0); } return WriteStringLoop(thread, buffer, str, start, end); } JSTaggedValue JSAPIFastBuffer::Fill(JSThread *thread, JSHandle &buffer, JSHandle &valueHandle, EncodingType encoding, uint32_t start, uint32_t end) { if (valueHandle->IsUndefined() || valueHandle->IsNull()) { valueHandle = JSHandle(thread, JSTaggedValue(0)); } if (valueHandle->IsNumber()) { if (!valueHandle->IsInt()) { std::ostringstream oss; oss << "Buffer fill number shoule be a integer"; JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, oss.str().c_str()); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Undefined()); } // only use low 8 bits of value uint8_t value = static_cast(valueHandle->GetInt()); uint32_t length = end - start; uint8_t *data = GetUnderlyingData(thread, buffer.GetTaggedValue(), start); if (data == nullptr) { JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::ARRAY_BUFFER_IS_NULL_OR_DETACHED, "The underlying ArrayBuffer is null or detached."); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } if (memset_s(data, length, value, length) != EOK) { JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, "Fill value memset failed."); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } } else if (valueHandle->IsString()) { FillString(thread, buffer, valueHandle, encoding, start, end); } else if (valueHandle->IsJSAPIBuffer() || valueHandle->IsTypedArray()) { WriteBufferObjectLoop(thread, buffer, valueHandle, start, end); } return buffer.GetTaggedValue(); } JSTaggedValue JSAPIFastBuffer::Compare(JSThread *thread, JSHandle &a, JSHandle &bObj, uint32_t sStart, uint32_t sEnd, uint32_t tStart, uint32_t tEnd) { JSTypedArray *typedArrayA = GetUInt8ArrayFromBufferObject(thread, a.GetTaggedValue()); JSTypedArray *typedArrayB = GetUInt8ArrayFromBufferObject(thread, bObj); uint32_t aCmpLength = sEnd - sStart; uint32_t bCmpLength = tEnd - tStart; if (typedArrayA->GetByteLength() < aCmpLength || typedArrayB->GetByteLength() < bCmpLength) { std::ostringstream oss; oss << "Buffer Compare parameters are out of range."; JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, oss.str().c_str()); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } uint8_t *aPointer = GetUnderlyingData(thread, a.GetTaggedValue(), sStart); uint8_t *bPointer = GetUnderlyingData(thread, bObj.GetTaggedValue(), tStart); if (aPointer == nullptr || bPointer == nullptr) { JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::ARRAY_BUFFER_IS_NULL_OR_DETACHED, "Buffer Compare memory ptr is nullptr."); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } uint32_t length = std::min(aCmpLength, bCmpLength); int32_t ret = memcmp(aPointer, bPointer, length); if (ret == 0) { if (aCmpLength != bCmpLength) { return aCmpLength < bCmpLength ? JSTaggedValue(-1) : JSTaggedValue(1); } return JSTaggedValue(0); } return ret < 0 ? JSTaggedValue(-1) : JSTaggedValue(1); } JSTaggedValue JSAPIFastBuffer::Includes(JSThread *thread, JSHandle &buffer, JSHandle valueHandle, uint32_t start, EncodingType encoding) { JSTaggedValue res = IndexOf(thread, buffer, valueHandle, start, encoding, false); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); return res.GetInt() == -1 ? JSTaggedValue::False() : JSTaggedValue::True(); } int32_t JSAPIFastBuffer::StringMatch(JSThread *thread, JSHandle &buffer, const uint8_t *str, uint32_t start, uint32_t strLength, bool isReverse) { if (buffer->GetLength() < strLength) { return -1; } auto src = GetUnderlyingData(thread, buffer.GetTaggedValue()); if (src == nullptr) { return -1; } uint32_t end = buffer->GetLength() - strLength; // reverse is mean enumeration order if (isReverse) { for (int32_t i = std::min(end, start); i >= 0; --i) { if (memcmp(str, src + i, strLength) == 0) { return i; } } } else { for (uint32_t i = start; i <= end; ++i) { if (memcmp(str, src + i, strLength) == 0) { return i; } } } return -1; } JSTaggedValue JSAPIFastBuffer::IndexOf(JSThread *thread, JSHandle &buffer, JSHandle valueHandle, uint32_t start, EncodingType encoding, bool isReverse) { if (!valueHandle->IsNumber()) { if (valueHandle->IsString()) { string str = GetString(thread, valueHandle, encoding); const uint8_t *data = reinterpret_cast(str.data()); uint32_t len = str.length(); return len == 0 ? JSTaggedValue(0) : JSTaggedValue(StringMatch(thread, buffer, data, start, len, isReverse)); } else if (valueHandle->IsJSAPIBuffer() || valueHandle->IsJSUint8Array()) { const uint8_t *data = GetUnderlyingData(thread, valueHandle.GetTaggedValue()); uint32_t len = valueHandle->IsJSAPIBuffer() ? JSHandle(valueHandle)->GetLength() : JSHandle(valueHandle)->GetByteLength(); if (len == 0) { return JSTaggedValue(0); } if (data == nullptr) { JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::ARRAY_BUFFER_IS_NULL_OR_DETACHED, "The underlying ArrayBuffer is null or detached."); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } return JSTaggedValue(StringMatch(thread, buffer, data, start, len, isReverse)); } JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, "IndexOf value invalid."); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } if (!valueHandle->IsInt()) { std::ostringstream oss; oss << "Buffer includes value shoule be integer when value is number"; JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, oss.str().c_str()); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } uint8_t value = static_cast(valueHandle->GetInt()); uint32_t length = buffer->GetLength(); if (isReverse) { for (int32_t i = std::min(length - 1, start); i >= 0; --i) { if (value == ReadUInt8(thread, buffer, i, true).GetInt()) { return JSTaggedValue(i); } } } else { for (uint32_t i = start; i < length; ++i) { if (value == ReadUInt8(thread, buffer, i, true).GetInt()) { return JSTaggedValue(i); } } } return JSTaggedValue(-1); // value not found } bool JSAPIFastBuffer::WriteBytes(JSThread *thread, const uint8_t *src, unsigned int size, uint8_t *dest) { if (src == nullptr || dest == nullptr) { return false; } if (size == 0) { return true; } if (memmove_s(dest, size, src, size) != EOK) { std::ostringstream oss; oss << "Buffer WriteBytes memmove_s failed"; JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, oss.str().c_str()); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, false); } return true; } bool JSAPIFastBuffer::WriteDataLoop(const uint8_t *src, uint8_t *dest, uint32_t length, uint32_t start, uint32_t end) { if (end - start <= 0 || length == 0) { return false; } dest += start; end -= start; uint32_t pos = 0; uint32_t copySize = std::min(length, end); if (memmove_s(dest, copySize, src, copySize) != EOK) { return false; } pos += copySize; while (pos < end) { copySize = std::min(copySize, end - pos); if (memmove_s(dest + pos, copySize, dest, copySize) != EOK) { return false; } pos += copySize; copySize <<= 1; } return true; } JSTaggedValue JSAPIFastBuffer::WriteStringLoop(JSThread *thread, JSHandle &buffer, string_view data, uint32_t start, uint32_t end) { uint32_t length = data.length(); uint8_t *dst = GetUnderlyingData(thread, buffer.GetTaggedValue()); uint8_t *str = const_cast(reinterpret_cast(data.data())); if (str == nullptr || dst == nullptr) { JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::ARRAY_BUFFER_IS_NULL_OR_DETACHED, "The underlying ArrayBuffer is null or detached."); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } if (!WriteDataLoop(str, dst, length, start, end)) { JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, "Write data Failed."); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } return JSTaggedValue(length); } JSTaggedValue JSAPIFastBuffer::WriteBufferObjectLoop(JSThread *thread, JSHandle &buffer, JSHandle &srcObj, uint32_t start, uint32_t end) { if (end - start > buffer->GetLength()) { return JSTaggedValue(0); } auto srcArray = GetUInt8ArrayFromBufferObject(thread, srcObj); uint32_t length = srcArray->GetByteLength(); uint8_t *src = GetUnderlyingData(thread, srcObj.GetTaggedValue()); uint8_t *dst = GetUnderlyingData(thread, buffer.GetTaggedValue()); if (src == nullptr || dst == nullptr) { JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::ARRAY_BUFFER_IS_NULL_OR_DETACHED, "The underlying ArrayBuffer is null or detached."); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } WriteDataLoop(src, dst, length, start, end); return JSTaggedValue(length); } bool JSAPIFastBuffer::WriteBytes(JSThread *thread, JSHandle srcData, JSHandle dstData, uint32_t sStart, uint32_t tStart, uint32_t size) { ASSERT(srcData->IsByteArray() || srcData->IsArrayBuffer()); ASSERT(dstData->IsByteArray() || dstData->IsArrayBuffer()); auto src = reinterpret_cast( builtins::BuiltinsArrayBuffer::GetDataPointFromBuffer(thread, srcData.GetTaggedValue(), sStart)); auto dst = reinterpret_cast( builtins::BuiltinsArrayBuffer::GetDataPointFromBuffer(thread, dstData.GetTaggedValue(), tStart)); return WriteBytes(thread, src, size, dst); } JSTaggedValue JSAPIFastBuffer::SetValueByIndex(JSThread *thread, const JSTaggedValue typedarray, uint32_t index, JSTaggedValue value, JSType valueType, bool littleEndian) { ASSERT(typedarray.IsJSUint8Array()); JSTypedArray *typedarrayObj = JSTypedArray::Cast(typedarray.GetTaggedObject()); if (UNLIKELY(value.IsECMAObject())) { return JSTaggedValue::Hole(); } JSTaggedValue buffer = typedarrayObj->GetViewedArrayBufferOrByteArray(thread); DataViewType elementType = TypedArrayHelper::GetType(valueType); auto valueHandle = JSHandle(thread, value); return BuiltinsArrayBuffer::SetValueInBuffer(thread, buffer, index, elementType, valueHandle, littleEndian); } JSTaggedValue JSAPIFastBuffer::GetValueByIndex(JSThread *thread, const JSTaggedValue typedarray, uint32_t index, JSType jsType, bool littleEndian) { ASSERT(typedarray.IsTypedArray() || typedarray.IsSharedTypedArray()); // Let buffer be the value of O’s [[ViewedArrayBuffer]] internal slot. JSTypedArray *typedarrayObj = JSTypedArray::Cast(typedarray.GetTaggedObject()); JSTaggedValue buffer = typedarrayObj->GetViewedArrayBufferOrByteArray(thread); if (buffer.IsArrayBuffer() && BuiltinsArrayBuffer::IsDetachedBuffer(thread, buffer)) { return JSTaggedValue::Undefined(); } DataViewType elementType = TypedArrayHelper::GetType(jsType); return BuiltinsArrayBuffer::GetValueFromBuffer(thread, buffer, index, elementType, littleEndian); } OperationResult JSAPIFastBuffer::GetProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key) { uint32_t length = obj->GetLength(); int32_t index = key->GetInt(); if (index < 0 || static_cast(index) >= length) { std::ostringstream oss; oss << "The value of \"index\" is out of range. It must be >= 0 and <= " << (length - 1) << ". Received value is: " << index; JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, oss.str().c_str()); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, OperationResult(thread, JSTaggedValue::Exception(), PropertyMetaData(false))); } return OperationResult(thread, ReadUInt8(thread, obj, index, true), PropertyMetaData(false)); } bool JSAPIFastBuffer::SetProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, const JSHandle &value) { uint32_t length = obj->GetLength(); int32_t index = key->GetInt(); if (index < 0 || static_cast(index) >= length) { return false; } WriteUInt8(thread, obj, value, index, true); return true; } void StringConverter::Base64Encode(const unsigned char *src, uint32_t len, string &outStr, bool isUrl) { if (!len) { outStr = ""; return; } char *out = nullptr; char *pos = nullptr; const unsigned char *pEnd = nullptr; const unsigned char *pStart = nullptr; size_t outLen = 4 * ((len + 2) / 3); // 3-byte blocks to 4-byte outStr.resize(outLen); out = outStr.data(); pEnd = src + len; pStart = src; pos = out; const char *table = isUrl ? "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_" : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; // 3 : 3 bytes is just 24 bits which is 4 times of 6 bits while (pEnd - pStart >= 3) { // 2 : add two zeros in front of the first set of 6 bits to become a new 8 binary bits *pos = table[pStart[0] >> 2]; // 4 : add two zeros in front of the following second set of 6 bits to become the new 8 binary bits *(pos + 1) = table[((pStart[0] & LOWER_2_BITS_MASK) << 4) | (pStart[1] >> 4)]; // 1 means the first encoded // 2 : 4 : 6 : add two zeros in front of the following third set of 6 bits to become the new 8 binary bits *(pos + 2) = table[((pStart[1] & LOWER_4_BITS_MASK) << 2) | (pStart[2] >> 6)]; // 2 : 3 : add two zeros in front of the following forth set of 6 bits to become the new 8 binary bits *(pos + 3) = table[pStart[2] & LOWER_6_BITS_MASK]; // 4 : the pointer of pos scrolls off 4 bytes to point the next 4 bytes of encoded chars pos += 4; // 3 : the pointer of pStart scrolls off 3 bytes to point the next 3 bytes of which will be encoded chars pStart += 3; } if (pEnd - pStart > 0) { // 2 : add two zeros in front of the first set of 6 bits to become a new 8 binary bits *pos = table[pStart[0] >> 2]; if (pEnd - pStart == 1) { // 4 : paddle the last two bits of the last byte with two zeros in front of it and four zeros after it *(pos + 1) = table[(pStart[0] & LOWER_2_BITS_MASK) << 4]; // 2 : fill in the missing bytes with '=' *(pos + 2) = '='; } else { // 4 : add two zeros in front of the second set of 6 bits to become the new 8 binary bits *(pos + 1) = table[((pStart[0] & LOWER_2_BITS_MASK) << 4) | (pStart[1] >> 4)]; // 2 : paddle the last four bits of the last byte with two zeros in front of it and two zeros after it *(pos + 2) = table[(pStart[1] & LOWER_4_BITS_MASK) << 2]; } // 3 : fill in the missing bytes with '=' *(pos + 3) = '='; } if (isUrl) { size_t poss = outStr.find_last_not_of('='); if (poss != std::string::npos) { outStr.erase(poss + 1); } } } void DecodeBase64CharSlow(const uint8_t *data, string &ret, size_t len, uint32_t &cursor, uint32_t &retIter) { auto &table = BASE64_TABLE; size_t count = 0; // 4 : four char to decode each time CVector temp(4, 0); // 4 : try to map 4 index from base64 table while (cursor < len && count < 4) { unsigned char ch = table[data[cursor++]]; if (ch > StringConverter::LOWER_6_BITS_MASK) { continue; } temp[count++] = ch; } if (count > JSAPIFastBuffer::OneByte) { ret[retIter] = // 2 : 4 : two bits(except two higer bits) of the second byte, combine them to a new byte ((temp[0] & StringConverter::LOWER_6_BITS_MASK) << 2) | ((temp[JSAPIFastBuffer::OneByte] & 0x30) >> 4); ++retIter; } if (count > JSAPIFastBuffer::TwoBytes) { // 4 : 2 : four bits(except two higer bits) of the third byte, combine them to a new byte ret[retIter] = ((temp[JSAPIFastBuffer::OneByte] & StringConverter::LOWER_4_BITS_MASK) << 4) | // 2 : (except two higer bits) ((temp[JSAPIFastBuffer::TwoBytes] & StringConverter::MIDDLE_4_BITS_MASK) >> 2); ++retIter; } if (count > JSAPIFastBuffer::ThreeBytes) { // 2 : 3 : 6 : combine them to a new byte ret[retIter] = ((temp[JSAPIFastBuffer::TwoBytes] & StringConverter::LOWER_2_BITS_MASK) << 6) | (temp[JSAPIFastBuffer::ThreeBytes] & StringConverter::LOWER_6_BITS_MASK); ++retIter; } } void StringConverter::Base64Decode(string_view encodedStr, string &ret) { size_t len = encodedStr.length(); unsigned int cursor = 0; unsigned char charArray4[4] = {0}; // why upperLength: len = ceil(decodedString.length / 3) * 4 // upperLength = len / 4 * 3 = ceil(decodedString.length / 3) * 3 >= // decodedString.length uint32_t upperLength = (len + 3) / 4 * 3; ret.reserve(upperLength); ret.resize(upperLength); uint32_t maxLen = len / 4 * 4; uint32_t retIter = 0; auto &table = BASE64_TABLE; const uint8_t *data = reinterpret_cast(encodedStr.data()); while (cursor < maxLen) { // 0 : the first index map to base64 table charArray4[0] = static_cast(table[data[cursor]]); // 1 : the second index map to base64 table charArray4[1] = static_cast(table[data[cursor + 1]]); // 2 : the third index map to base64 table charArray4[2] = static_cast(table[data[cursor + 2]]); // 3 : the fourth index map to base64 table charArray4[3] = static_cast(table[data[cursor + 3]]); // 0 : 1 : 2 : 3 : check if invalid index exist if ((charArray4[0] | charArray4[1] | charArray4[2] | charArray4[3]) > LOWER_6_BITS_MASK) { DecodeBase64CharSlow(data, ret, len, cursor, retIter); continue; } // 1 : result first char // 2 : 4 : two bits(except two higer bits) of the second byte, combine them to a new byte ret[retIter] = ((charArray4[0] & LOWER_6_BITS_MASK) << 2) | ((charArray4[1] & 0x30) >> 4); // 1 : 2 : result second char // 4 : 2 : four bits(except two higer bits) of the third byte, combine them to a new byte ret[retIter + 1] = ((charArray4[1] & LOWER_4_BITS_MASK) << 4) | ((charArray4[2] & MIDDLE_4_BITS_MASK) >> 2); // 2 : 3 : result third char // 2 : 3 : 6 : combine them to a new byte ret[retIter + 2] = ((charArray4[2] & LOWER_2_BITS_MASK) << 6) | (charArray4[3] & LOWER_6_BITS_MASK); // 3 : every time 3 bytes retIter += 3; // 4 : every time 4 bytes cursor += 4; } if (cursor < len) { DecodeBase64CharSlow(data, ret, len, cursor, retIter); } ret.resize(retIter); } // encoding bytesflow(latin1) to utf8 void StringConverter::Latin1Encode(const unsigned char *data, uint32_t len, std::string &ret) { // 2 : reserve enough space to encode ret.reserve(len * 2); for (uint32_t i = 0; i < len; i++) { unsigned char c = data[i]; if (c < 0x80) { ret.push_back(c); } else { ret.push_back(0xc0 | (c >> UTF8_VALID_BITS)); ret.push_back(0x80 | (c & 0x3f)); } } } bool IsValidHex(const string &hex) { if (!hex.length()) { return false; } for (unsigned int i = 0; i < hex.size(); i++) { char c = hex.at(i); // 0 ~ 9, A ~ F, a ~ f if (!(c <= '9' && c >= '0') && !(c <= 'F' && c >= 'A') && !(c <= 'f' && c >= 'a')) { return false; } } return true; } void StringConverter::HexEncode(const unsigned char *hexStr, uint32_t len, string &ret) { // 2 : reserve enough length to encode ret.reserve(2 * len); for (uint32_t i = 0; i < len; i++) { uint8_t high = hexStr[i] >> 4; uint8_t low = hexStr[i] & 0x0f; // 9 : 10 : convert high to hex ret.push_back(high > 9 ? 'a' + (high % 10) : '0' + high); // 9 : 10 : convert low to hex ret.push_back(low > 9 ? 'a' + (low % 10) : '0' + low); } } void StringConverter::HexDecode(string_view hexStr, string &ret) { unsigned int arrSize = hexStr.size(); // 2 : 1 : means a half length of hex str's size ret.reserve(arrSize / 2); string hexStrTmp; int num = 0; // 2 : hex string to stoi hexStrTmp.resize(2); // 2 : means a half length of hex str's size for (unsigned int i = 0; i < arrSize / 2; i++) { // 2 : offset is i * 2 hexStrTmp[0] = hexStr[i * 2]; // 2 : offset is i * 2 + 1 hexStrTmp[1] = hexStr[i * 2 + 1]; if (!IsValidHex(hexStrTmp)) { break; } // 16 : the base is 16 num = stoi(hexStrTmp, nullptr, 16); ret.push_back(static_cast(num)); } } std::pair SplitUInt(JSHandle &value, uint64_t byteLength) { ASSERT(value->IsNumber()); uint64_t temp; if (value->IsDouble()) { temp = static_cast(value->GetDouble()); } else { temp = static_cast(value->GetInt()); } byteLength *= JSAPIFastBuffer::ONE_BYTE_BIT_LENGTH; uint64_t bitMask = static_cast(static_cast(1) << byteLength) - 1; auto l = JSTaggedValue(static_cast(temp & bitMask)); auto r = JSTaggedValue(static_cast(temp >> byteLength)); return std::make_pair(l, r); } JSTaggedValue MergeUInt(JSTaggedValue low, JSTaggedValue high, uint64_t byteLengthLow) { byteLengthLow *= JSAPIFastBuffer::ONE_BYTE_BIT_LENGTH; uint64_t lowValue = 0; uint64_t highValue = 0; lowValue |= static_cast(low.GetNumber()); highValue |= static_cast(high.GetNumber()); return JSTaggedValue(static_cast((highValue << byteLengthLow) | lowValue)); } JSTaggedValue JSAPIFastBuffer::WriteBytesValue(JSThread *thread, JSHandle &buffer, JSHandle &value, uint32_t offset, ByteLength byteLength, bool littleEndian) { if (UNLIKELY(byteLength > SixBytes)) { JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, "WriteInt or WriteUInt only support 1 byte to 6 bytes"); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } switch (byteLength) { case OneByte: return WriteUInt8(thread, buffer, value, offset, littleEndian); case TwoBytes: return WriteUInt16(thread, buffer, value, offset, littleEndian); case FourBytes: return WriteUInt32(thread, buffer, value, offset, littleEndian); default: break; } if (byteLength > FourBytes) { std::pair valuePair = SplitUInt(value, ThreeBytes); if (valuePair.first == JSTaggedValue::Undefined()) { JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, "WriteInt or WriteUInt only support Integer Type"); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } JSHandle lowValue = JSHandle(thread, valuePair.first); JSHandle highValue = JSHandle(thread, valuePair.second); ByteLength highBytes = static_cast(byteLength - ThreeBytes); if (littleEndian) { auto nextIndex = WriteBytesValue(thread, buffer, lowValue, offset, ThreeBytes, littleEndian); return WriteBytesValue(thread, buffer, highValue, nextIndex.GetInt(), highBytes, littleEndian); } auto nextIndex = WriteBytesValue(thread, buffer, highValue, offset, highBytes, littleEndian); return WriteBytesValue(thread, buffer, lowValue, nextIndex.GetInt(), ThreeBytes, littleEndian); } std::pair valuePair = SplitUInt(value, TwoBytes); if (valuePair.first == JSTaggedValue::Undefined()) { JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, "WriteInt or WriteUInt only support Integer Type"); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } JSHandle lowValue = JSHandle(thread, valuePair.first); JSHandle highValue = JSHandle(thread, valuePair.second); ByteLength highBytes = static_cast(byteLength - TwoBytes); if (littleEndian) { auto nextIndex = WriteBytesValue(thread, buffer, lowValue, offset, TwoBytes, littleEndian); return WriteBytesValue(thread, buffer, highValue, nextIndex.GetInt(), highBytes, littleEndian); } auto nextIndex = WriteBytesValue(thread, buffer, highValue, offset, highBytes, littleEndian); return WriteBytesValue(thread, buffer, lowValue, nextIndex.GetInt(), TwoBytes, littleEndian); } JSTaggedValue JSAPIFastBuffer::ReadBytes(JSThread *thread, JSHandle &buffer, uint32_t offset, ByteLength byteLength, bool littleEndian) { if (UNLIKELY(byteLength > SixBytes || byteLength < OneByte)) { std::ostringstream oss; oss << "ReadInt or ReadUInt only support 1 byte to 6 bytes"; JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, oss.str().c_str()); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } switch (byteLength) { case OneByte: return ReadUInt8(thread, buffer, offset, littleEndian); case TwoBytes: return ReadUInt16(thread, buffer, offset, littleEndian); case FourBytes: return ReadUInt32(thread, buffer, offset, littleEndian); default: break; } if (byteLength > FourBytes) { auto valueLow = ReadBytes(thread, buffer, offset, ThreeBytes, littleEndian); ByteLength highBytes = static_cast(byteLength - ThreeBytes); auto valueHigh = ReadBytes(thread, buffer, offset + ThreeBytes, highBytes, littleEndian); if (littleEndian) { return MergeUInt(valueLow, valueHigh, ThreeBytes); } return MergeUInt(valueHigh, valueLow, highBytes); } auto valueLow = ReadBytes(thread, buffer, offset, TwoBytes, littleEndian); ByteLength highBytes = static_cast(byteLength - TwoBytes); auto valueHigh = ReadBytes(thread, buffer, offset + TwoBytes, highBytes, littleEndian); if (littleEndian) { return MergeUInt(valueLow, valueHigh, TwoBytes); } return MergeUInt(valueHigh, valueLow, highBytes); } JSTaggedValue JSAPIFastBuffer::ReadInt(JSThread *thread, JSHandle &buffer, uint32_t offset, ByteLength byteLength, bool littleEndian) { auto ret = ReadBytes(thread, buffer, offset, byteLength, littleEndian); int64_t value = ret.GetNumber(); // 1 : calculate bit mask int64_t negetiveMask = (1LL << (byteLength * JSAPIFastBuffer::ONE_BYTE_BIT_LENGTH - 1)); if (value & negetiveMask) { int64_t bitMask = negetiveMask | (negetiveMask - 1); bitMask &= -value; // -1 : to negetive return JSTaggedValue(JSTaggedNumber(JSTaggedValue(bitMask)) * JSTaggedNumber(-1)); } return JSTaggedValue(value); } JSTaggedValue JSAPIFastBuffer::WriteBigUInt64(JSThread *thread, const JSHandle &buffer, const JSHandle &value, uint32_t offset, bool littleEndian) { JSType type = JSType::JS_BIGUINT64_ARRAY; auto byteSize = base::TypedArrayHelper::GetElementSize(type); JSHandle typedArray = JSHandle(thread, JSTypedArray::Cast(buffer->GetFastBufferData(thread).GetTaggedObject())); uint64_t valueNum; bool isLossLess; ASSERT(value->IsBigInt() || value->IsNumber() || value->IsBoolean()); BigInt::BigIntToUint64(thread, value, &valueNum, &isLossLess); uint64_t left = 0; uint64_t right = UINT64_MAX; if (valueNum > right || valueNum < left) { std::ostringstream oss; oss << std::fixed << "The value of \"value\" is out of range. It must be >= " << left << " and <= " << right << ". Received value is: " << 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 < 0 || offset + NumberSize::BIGUINT64 > buffer->GetLength()) { std::ostringstream oss; oss << std::fixed << "The value of \"offset\" is out of range. It must be >= " << 0 << " and <= " << buffer->GetLength() - NumberSize::BIGUINT64 << ". 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(), offset, value.GetTaggedValue(), type, littleEndian); return JSTaggedValue(offset + byteSize); } JSTaggedValue JSAPIFastBuffer::ReadBigUInt64(JSThread *thread, const JSHandle &buffer, uint32_t offset, bool littleEndian) { JSType type = JSType::JS_BIGUINT64_ARRAY; JSHandle typedArray = JSHandle(thread, JSTypedArray ::Cast(buffer->GetFastBufferData(thread).GetTaggedObject())); if (offset < 0 || offset + NumberSize::BIGUINT64 > buffer->GetLength()) { std::ostringstream oss; oss << std::fixed << "The value of \"offset\" is out of range. It must be >= " << 0 << " and <= " << buffer->GetLength() - NumberSize::BIGUINT64 << ". 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(), offset, type, littleEndian); } JSTaggedValue JSAPIFastBuffer::WriteBigInt64(JSThread *thread, const JSHandle &buffer, const JSHandle &value, uint32_t offset, bool littleEndian) { JSType type = JSType::JS_BIGINT64_ARRAY; auto byteSize = base::TypedArrayHelper::GetElementSize(type); JSHandle typedArray = JSHandle(thread, JSTypedArray ::Cast(buffer->GetFastBufferData(thread).GetTaggedObject())); int64_t valueNum; bool isLossLess; ASSERT(value->IsBigInt() || value->IsNumber() || value->IsBoolean()); BigInt::BigIntToInt64(thread, value, &valueNum, &isLossLess); int64_t left = INT64_MIN; int64_t right = INT64_MAX; if (valueNum > right || valueNum < left) { std::ostringstream oss; oss << std::fixed << "The value of \"value\" is out of range. It must be >= " << left << " and <= " << right << ". Received value is: " << 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 < 0 || offset + NumberSize::BIGUINT64 > buffer->GetLength()) { std::ostringstream oss; oss << std::fixed << "The value of \"offset\" is out of range. It must be >= " << 0 << " and <= " << buffer->GetLength() - NumberSize::BIGUINT64 << ". 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(), offset, value.GetTaggedValue(), type, littleEndian); return JSTaggedValue(offset + byteSize); } JSTaggedValue JSAPIFastBuffer::ReadBigInt64(JSThread *thread, const JSHandle &buffer, uint32_t offset, bool littleEndian) { JSType type = JSType::JS_BIGINT64_ARRAY; JSHandle typedArray = JSHandle(thread, JSTypedArray ::Cast(buffer->GetFastBufferData(thread).GetTaggedObject())); if (offset < 0 || offset + NumberSize::BIGINT64 > buffer->GetLength()) { std::ostringstream oss; oss << std::fixed << "The value of \"offset\" is out of range. It must be >= " << 0 << " and <= " << buffer->GetLength() - NumberSize::BIGINT64 << ". 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(), offset, type, littleEndian); } } // namespace panda::ecmascript