• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022 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 #include "js_textdecoder.h"
17 
18 #include "ohos/init_data.h"
19 #include "securec.h"
20 #include "util_helper.h"
21 
22 namespace OHOS::Util {
23     using namespace Commonlibrary::Platform;
24 
TextDecoder(const std::string & buff,int32_t flags)25     TextDecoder::TextDecoder(const std::string &buff, int32_t flags)
26         : encStr_(buff), tranTool_(nullptr, nullptr)
27     {
28         label_ |= flags;
29 #if !defined(__ARKUI_CROSS__)
30         SetHwIcuDirectory();
31 #endif
32         bool fatal = (flags & static_cast<int32_t>(ConverterFlags::FATAL_FLG)) ==
33              static_cast<int32_t>(ConverterFlags::FATAL_FLG);
34         UErrorCode codeflag = U_ZERO_ERROR;
35         UConverter *conv = CreateConverter(encStr_, codeflag);
36         if (U_FAILURE(codeflag)) {
37             HILOG_ERROR("TextDecoder:: ucnv_open failed !");
38             return;
39         }
40         if (fatal) {
41             codeflag = U_ZERO_ERROR;
42             ucnv_setToUCallBack(conv, UCNV_TO_U_CALLBACK_STOP, nullptr, nullptr, nullptr, &codeflag);
43         }
44         TransformToolPointer tempTranTool(conv, ConverterClose);
45         tranTool_ = std::move(tempTranTool);
46     }
47 
48     //static
CanBeCompressed(const uint16_t * utf16Data,uint32_t utf16Len)49     bool TextDecoder::CanBeCompressed(const uint16_t *utf16Data, uint32_t utf16Len)
50     {
51         uint32_t index = 0;
52         for (; index + 4 <= utf16Len; index += 4) { // 4: process the data in chunks of 4 elements to improve speed
53             // Check if all four characters in the current block are ASCII characters
54             if (!IsASCIICharacter(utf16Data[index]) ||
55                 !IsASCIICharacter(utf16Data[index + 1]) || // 1: the second element of the block
56                 !IsASCIICharacter(utf16Data[index + 2]) || // 2: the third element of the block
57                 !IsASCIICharacter(utf16Data[index + 3])) { // 3: the fourth element of the block
58                 return false;
59             }
60         }
61         // Check remaining characters if they are ASCII
62         for (; index < utf16Len; ++index) {
63             if (!IsASCIICharacter(utf16Data[index])) {
64                 return false;
65             }
66         }
67         return true;
68     }
69 
ConvertToChar(UChar * uchar,size_t length,char * tempCharArray)70     std::pair<char *, bool> TextDecoder::ConvertToChar(UChar *uchar, size_t length, char *tempCharArray)
71     {
72         uint16_t *uint16Data = reinterpret_cast<uint16_t *>(uchar);
73         if (CanBeCompressed(uint16Data, length)) {
74             if (length <= 0) {
75                 HILOG_ERROR("TextDecoder:: length is error");
76                 return std::make_pair(nullptr, false);
77             }
78             char *strUtf8;
79             if (length <= TEMP_CHAR_LENGTH) {
80                 strUtf8 = tempCharArray;
81             } else {
82                 strUtf8 = new (std::nothrow) char[length];
83                 if (strUtf8 == nullptr) {
84                     HILOG_ERROR("TextDecoder:: memory allocation failed, strUtf8 is nullptr");
85                     return std::make_pair(nullptr, false);
86                 }
87             }
88             for (size_t i = 0; i < length; ++i) {
89                 strUtf8[i] = static_cast<char>(uchar[i]);
90             }
91             return std::make_pair(strUtf8, true);
92         }
93         return std::make_pair(nullptr, false);
94     }
95 
Decode(napi_env env,napi_value src,bool iflag)96     napi_value TextDecoder::Decode(napi_env env, napi_value src, bool iflag)
97     {
98         uint8_t flags = 0;
99         flags |= (iflag ? 0 : static_cast<uint8_t>(ConverterFlags::FLUSH_FLG));
100         UBool flush = ((flags & static_cast<uint8_t>(ConverterFlags::FLUSH_FLG))) ==
101         static_cast<uint8_t>(ConverterFlags::FLUSH_FLG);
102         napi_typedarray_type type;
103         size_t length = 0;
104         void *data = nullptr;
105         size_t byteOffset = 0;
106         napi_value arrayBuffer = nullptr;
107         NAPI_CALL(env, napi_get_typedarray_info(env, src, &type, &length, &data, &arrayBuffer, &byteOffset));
108         const char *source = ReplaceNull(data, length);
109         size_t limit = GetMinByteSize() * length;
110         size_t len = limit * sizeof(UChar);
111         UChar *arr = nullptr;
112         if (limit > 0) {
113             arr = new (std::nothrow) UChar[limit + 1];
114             if (arr == nullptr) {
115                 HILOG_ERROR("TextDecoder:: memory allocation failed, decode arr is nullptr");
116                 return nullptr;
117             }
118             if (memset_s(arr, len + sizeof(UChar), 0, len + sizeof(UChar)) != EOK) {
119                 HILOG_ERROR("TextDecoder:: decode arr memset_s failed");
120                 FreedMemory(arr);
121                 return nullptr;
122             }
123         } else {
124             HILOG_DEBUG("TextDecoder:: limit is error");
125             return nullptr;
126         }
127         UChar *target = arr;
128         size_t tarStartPos = reinterpret_cast<uintptr_t>(arr);
129         UErrorCode codeFlag = U_ZERO_ERROR;
130         ucnv_toUnicode(GetConverterPtr(), &target, target + len, &source, source + length, nullptr, flush, &codeFlag);
131         if (codeFlag != U_ZERO_ERROR) {
132             return ThrowError(env, "TextDecoder decoding error.");
133         }
134         size_t resultLength = 0;
135         bool omitInitialBom = false;
136         DecodeArr decArr(target, tarStartPos, limit);
137         SetBomFlag(arr, codeFlag, decArr, resultLength, omitInitialBom);
138         UChar *arrDat = arr;
139         if (omitInitialBom && resultLength > 0) {
140             arrDat = &arr[2]; // 2: Obtains the 2 value of the array.
141         }
142         std::string tepStr = ConvertToString(arrDat, length);
143         napi_value resultStr = nullptr;
144         NAPI_CALL(env, napi_create_string_utf8(env, tepStr.c_str(), tepStr.size(), &resultStr));
145         FreedMemory(arr);
146         if (flush) {
147             label_ &= static_cast<int32_t>(ConverterFlags::BOM_SEEN_FLG);
148             Reset();
149         }
150         return resultStr;
151     }
152 
GetResultStr(napi_env env,UChar * arrDat,size_t length)153     napi_value TextDecoder::GetResultStr(napi_env env, UChar *arrDat,
154                                          size_t length)
155     {
156         napi_value resultStr = nullptr;
157         if (length <= TEMP_CHAR_LENGTH) {
158             char tempCharArray[TEMP_CHAR_LENGTH];
159             std::pair<char *, bool> tempPair = ConvertToChar(arrDat, length, tempCharArray);
160             if (tempPair.second == true) {
161                 char *utf8Str = tempPair.first;
162                 napi_create_string_utf8(env, utf8Str, length, &resultStr);
163             } else {
164                 napi_create_string_utf16(env, reinterpret_cast<char16_t *>(arrDat), length, &resultStr);
165             }
166         } else {
167             std::pair<char *, bool> tempPair = ConvertToChar(arrDat, length, nullptr);
168             if (tempPair.second == true) {
169                 char *utf8Str = tempPair.first;
170                 napi_create_string_utf8(env, utf8Str, length, &resultStr);
171                 NAPI_ASSERT(env, utf8Str != nullptr, "Data allocation failed");
172                 delete[] utf8Str;
173             } else {
174                 napi_create_string_utf16(env, reinterpret_cast<char16_t *>(arrDat), length, &resultStr);
175             }
176         }
177         return resultStr;
178     }
179 
DecodeToString(napi_env env,napi_value src,bool iflag)180     napi_value TextDecoder::DecodeToString(napi_env env,
181                                            napi_value src, bool iflag)
182     {
183         uint8_t flags = 0;
184         flags |= (iflag ? 0 : static_cast<uint8_t>(ConverterFlags::FLUSH_FLG));
185         UBool flush = (flags & static_cast<uint8_t>(ConverterFlags::FLUSH_FLG)) ==
186             static_cast<uint8_t>(ConverterFlags::FLUSH_FLG);
187         napi_typedarray_type type;
188         size_t length = 0;
189         void *data = nullptr;
190         size_t byteOffset = 0;
191         napi_value arrayBuffer = nullptr;
192         napi_get_typedarray_info(env, src, &type, &length, &data, &arrayBuffer, &byteOffset);
193         const char *source = static_cast<char *>(data);
194         size_t limit = GetMinByteSize() * length;
195         size_t len = limit * sizeof(UChar);
196         UChar *arr = nullptr;
197         if (limit > 0) {
198             arr = new (std::nothrow) UChar[limit + 1]{0};
199             if (arr == nullptr) {
200                 HILOG_ERROR("TextDecoder:: memory allocation failed, arr is nullptr");
201                 return nullptr;
202             }
203         } else {
204             HILOG_DEBUG("TextDecoder:: limit is error");
205             return nullptr;
206         }
207         UChar *target = arr;
208         UErrorCode codeFlag = U_ZERO_ERROR;
209         ucnv_toUnicode(GetConverterPtr(), &target, target + len, &source, source + length, nullptr, flush, &codeFlag);
210         if (codeFlag != U_ZERO_ERROR) {
211             FreedMemory(arr);
212             napi_throw_error(env, "401",
213                 "Parameter error. Please check if the decode data matches the encoding format.");
214             return nullptr;
215         }
216         size_t resultLen = target - arr;
217         bool omitInitialBom = false;
218         SetIgnoreBOM(arr, resultLen, omitInitialBom);
219         UChar *arrDat = arr;
220         if (omitInitialBom) {
221             arrDat = &arr[1];
222             resultLen--;
223         }
224         napi_value resultStr = GetResultStr(env, arrDat, resultLen);
225         FreedMemory(arr);
226         if (flush) {
227             label_ &= ~static_cast<int32_t>(ConverterFlags::BOM_SEEN_FLG);
228             Reset();
229         }
230         return resultStr;
231     }
232 
GetMinByteSize() const233     size_t TextDecoder::GetMinByteSize() const
234     {
235         if (tranTool_ == nullptr) {
236             return 0;
237         }
238         size_t res = static_cast<size_t>(ucnv_getMinCharSize(tranTool_.get()));
239         return res;
240     }
241 
Reset() const242     void TextDecoder::Reset() const
243     {
244         if (tranTool_ == nullptr) {
245             return;
246         }
247         ucnv_reset(tranTool_.get());
248     }
249 
FreedMemory(UChar * & pData)250     void TextDecoder::FreedMemory(UChar *&pData)
251     {
252         if (pData != nullptr) {
253             delete[] pData;
254             pData = nullptr;
255         }
256     }
257 
SetBomFlag(const UChar * arr,const UErrorCode codeFlag,const DecodeArr decArr,size_t & rstLen,bool & bomFlag)258     void TextDecoder::SetBomFlag(const UChar *arr, const UErrorCode codeFlag, const DecodeArr decArr,
259                                  size_t &rstLen, bool &bomFlag)
260     {
261         if (arr == nullptr) {
262             return;
263         }
264         if (U_SUCCESS(codeFlag)) {
265             if (decArr.limitLen > 0) {
266                 rstLen = reinterpret_cast<uintptr_t>(decArr.target) - decArr.tarStartPos;
267                 if (rstLen > 0 && IsUnicode() && !IsIgnoreBom() && !IsBomFlag()) {
268                     bomFlag = (arr[0] == 0xFEFF) ? true : false;
269                     label_ |= static_cast<int32_t>(ConverterFlags::BOM_SEEN_FLG);
270                 }
271             }
272         }
273     }
274 
SetIgnoreBOM(const UChar * arr,size_t resultLen,bool & bomFlag)275     void TextDecoder::SetIgnoreBOM(const UChar *arr, size_t resultLen, bool &bomFlag)
276     {
277         switch (ucnv_getType(GetConverterPtr())) {
278             case UCNV_UTF8:
279             case UCNV_UTF16_BigEndian:
280             case UCNV_UTF16_LittleEndian:
281                 label_ |= static_cast<int32_t>(ConverterFlags::UNICODE_FLG);
282                 break;
283             default:
284                 break;
285         }
286         if (resultLen > 0 && IsUnicode() && IsIgnoreBom()) {
287             bomFlag = (arr[0] == 0xFEFF) ? true : false;
288         }
289         label_ |= static_cast<int32_t>(ConverterFlags::BOM_SEEN_FLG);
290     }
291 
ThrowError(napi_env env,const char * errMessage)292     napi_value TextDecoder::ThrowError(napi_env env, const char* errMessage)
293     {
294         napi_value utilError = nullptr;
295         napi_value code = nullptr;
296         uint32_t errCode = 10200019;
297         napi_create_uint32(env, errCode, &code);
298         napi_value name = nullptr;
299         std::string errName = "BusinessError";
300         napi_value msg = nullptr;
301         napi_create_string_utf8(env, errMessage, NAPI_AUTO_LENGTH, &msg);
302         napi_create_string_utf8(env, errName.c_str(), NAPI_AUTO_LENGTH, &name);
303         napi_create_error(env, nullptr, msg, &utilError);
304         napi_set_named_property(env, utilError, "code", code);
305         napi_set_named_property(env, utilError, "name", name);
306         napi_throw(env, utilError);
307         napi_value res = nullptr;
308         NAPI_CALL(env, napi_get_undefined(env, &res));
309         return res;
310     }
311 
ReplaceNull(void * data,size_t length) const312     const char* TextDecoder::ReplaceNull(void *data, size_t length) const
313     {
314         char *str = static_cast<char*>(data);
315         if (encStr_ == "utf-8") {
316             for (size_t i = 0; i < length; ++i) {
317                 if (str[i] == '\0') {
318                     str[i] = ' ';
319                 }
320             }
321         }
322         return const_cast<const char*>(str);
323     }
324 }
325