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 #include <algorithm>
18 #include <codecvt>
19 
20 #include <locale>
21 #include <map>
22 #include <string>
23 #include <vector>
24 
25 #include "ohos/init_data.h"
26 #include "securec.h"
27 #include "unicode/unistr.h"
28 #include "util_helper.h"
29 #include "tools/log.h"
30 
31 namespace OHOS::Util {
32     using namespace Commonlibrary::Platform;
33 
TextDecoder(const std::string & buff,std::vector<int> optionVec)34     TextDecoder::TextDecoder(const std::string &buff, std::vector<int> optionVec)
35         : label_(0), encStr_(buff), tranTool_(nullptr, nullptr)
36     {
37         label_ |= optionVec[0] ? static_cast<int32_t>(ConverterFlags::FATAL_FLG) : 0;
38         label_ |= optionVec[1] ? static_cast<int32_t>(ConverterFlags::IGNORE_BOM_FLG) : 0;
39 #if !defined(__ARKUI_CROSS__)
40         SetHwIcuDirectory();
41 #endif
42         bool fatal = (label_ & static_cast<int32_t>(ConverterFlags::FATAL_FLG)) ==
43              static_cast<int32_t>(ConverterFlags::FATAL_FLG);
44         UErrorCode codeflag = U_ZERO_ERROR;
45         UConverter *conv = CreateConverter(encStr_, codeflag);
46         if (U_FAILURE(codeflag)) {
47             HILOG_ERROR("ucnv_open failed !");
48             return;
49         }
50         if (fatal) {
51             codeflag = U_ZERO_ERROR;
52             ucnv_setToUCallBack(conv, UCNV_TO_U_CALLBACK_STOP, nullptr, nullptr, nullptr, &codeflag);
53         }
54         TransformToolPointer tempTranTool(conv, ConverterClose);
55         tranTool_ = std::move(tempTranTool);
56     }
57 
Decode(napi_env env,napi_value src,bool iflag)58     napi_value TextDecoder::Decode(napi_env env, napi_value src, bool iflag)
59     {
60         uint8_t flags = 0;
61         flags |= (iflag ? 0 : static_cast<uint8_t>(ConverterFlags::FLUSH_FLG));
62         UBool flush = ((flags & static_cast<uint8_t>(ConverterFlags::FLUSH_FLG))) ==
63         static_cast<uint8_t>(ConverterFlags::FLUSH_FLG);
64         napi_typedarray_type type;
65         size_t length = 0;
66         void *data = nullptr;
67         size_t byteOffset = 0;
68         napi_value arrayBuffer = nullptr;
69         NAPI_CALL(env, napi_get_typedarray_info(env, src, &type, &length, &data, &arrayBuffer, &byteOffset));
70         const char *source = ReplaceNull(data, length);
71         size_t limit = GetMinByteSize() * length;
72         size_t len = limit * sizeof(UChar);
73         UChar *arr = nullptr;
74         if (limit > 0) {
75             arr = new (std::nothrow) UChar[limit + 1];
76             if (arr == nullptr) {
77                 HILOG_ERROR("decode arr is nullptr");
78                 return nullptr;
79             }
80             if (memset_s(arr, len + sizeof(UChar), 0, len + sizeof(UChar)) != EOK) {
81                 HILOG_ERROR("decode arr memset_s failed");
82                 FreedMemory(arr);
83                 return nullptr;
84             }
85         } else {
86             HILOG_DEBUG("limit is error");
87             return nullptr;
88         }
89         UChar *target = arr;
90         size_t tarStartPos = reinterpret_cast<uintptr_t>(arr);
91         UErrorCode codeFlag = U_ZERO_ERROR;
92         ucnv_toUnicode(GetConverterPtr(), &target, target + len, &source, source + length, nullptr, flush, &codeFlag);
93         if (codeFlag != U_ZERO_ERROR) {
94             return ThrowError(env, "TextDecoder decoding error.");
95         }
96         size_t resultLength = 0;
97         bool omitInitialBom = false;
98         DecodeArr decArr(target, tarStartPos, limit);
99         SetBomFlag(arr, codeFlag, decArr, resultLength, omitInitialBom);
100         UChar *arrDat = arr;
101         if (omitInitialBom && resultLength > 0) {
102             arrDat = &arr[2]; // 2: Obtains the 2 value of the array.
103         }
104         std::string tepStr = ConvertToString(arrDat, length);
105         napi_value resultStr = nullptr;
106         NAPI_CALL(env, napi_create_string_utf8(env, tepStr.c_str(), tepStr.size(), &resultStr));
107         FreedMemory(arr);
108         if (flush) {
109             label_ &= static_cast<int32_t>(ConverterFlags::BOM_SEEN_FLG);
110             Reset();
111         }
112         return resultStr;
113     }
114 
DecodeToString(napi_env env,napi_value src,bool iflag)115     napi_value TextDecoder::DecodeToString(napi_env env, napi_value src, bool iflag)
116     {
117         uint8_t flags = 0;
118         flags |= (iflag ? 0 : static_cast<uint8_t>(ConverterFlags::FLUSH_FLG));
119         UBool flush = (flags & static_cast<uint8_t>(ConverterFlags::FLUSH_FLG)) ==
120             static_cast<uint8_t>(ConverterFlags::FLUSH_FLG);
121         napi_typedarray_type type;
122         size_t length = 0;
123         void *data = nullptr;
124         size_t byteOffset = 0;
125         napi_value arrayBuffer = nullptr;
126         napi_get_typedarray_info(env, src, &type, &length, &data, &arrayBuffer, &byteOffset);
127         const char *source = static_cast<char *>(data);
128         size_t limit = GetMinByteSize() * length;
129         size_t len = limit * sizeof(UChar);
130         UChar *arr = nullptr;
131         if (limit > 0) {
132             arr = new (std::nothrow) UChar[limit + 1]{0};
133             if (arr == nullptr) {
134                 HILOG_DEBUG("arr is nullptr");
135                 return nullptr;
136             }
137         } else {
138             HILOG_DEBUG("limit is error");
139             return nullptr;
140         }
141         UChar *target = arr;
142         UErrorCode codeFlag = U_ZERO_ERROR;
143         ucnv_toUnicode(GetConverterPtr(), &target, target + len, &source, source + length, nullptr, flush, &codeFlag);
144         if (codeFlag != U_ZERO_ERROR) {
145             FreedMemory(arr);
146             napi_throw_error(env, "401",
147                 "Parameter error. Please check if the decode data matches the encoding format.");
148             return nullptr;
149         }
150         size_t resultLen = target - arr;
151         bool omitInitialBom = false;
152         SetIgnoreBOM(arr, resultLen, omitInitialBom);
153         UChar *arrDat = arr;
154         if (omitInitialBom) {
155             arrDat = &arr[1];
156             resultLen--;
157         }
158         napi_value resultStr = nullptr;
159         napi_create_string_utf16(env, reinterpret_cast<char16_t *>(arrDat), resultLen, &resultStr);
160         FreedMemory(arr);
161         if (flush) {
162             label_ &= ~static_cast<int32_t>(ConverterFlags::BOM_SEEN_FLG);
163             Reset();
164         }
165         return resultStr;
166     }
167 
GetEncoding(napi_env env) const168     napi_value TextDecoder::GetEncoding(napi_env env) const
169     {
170         size_t length = encStr_.length();
171         napi_value result = nullptr;
172         NAPI_CALL(env, napi_create_string_utf8(env, encStr_.c_str(), length, &result));
173         return result;
174     }
175 
GetFatal(napi_env env) const176     napi_value TextDecoder::GetFatal(napi_env env) const
177     {
178         int32_t temp = label_ & static_cast<int32_t>(ConverterFlags::FATAL_FLG);
179         bool comRst = false;
180         if (temp == static_cast<int32_t>(ConverterFlags::FATAL_FLG)) {
181             comRst = true;
182         } else {
183             comRst = false;
184         }
185         napi_value result = nullptr;
186         NAPI_CALL(env, napi_get_boolean(env, comRst, &result));
187         return result;
188     }
189 
GetIgnoreBOM(napi_env env) const190     napi_value TextDecoder::GetIgnoreBOM(napi_env env) const
191     {
192         int32_t temp = label_ & static_cast<int32_t>(ConverterFlags::IGNORE_BOM_FLG);
193         bool comRst = false;
194         if (temp == static_cast<int32_t>(ConverterFlags::IGNORE_BOM_FLG)) {
195             comRst = true;
196         } else {
197             comRst = false;
198         }
199         napi_value result;
200         NAPI_CALL(env, napi_get_boolean(env, comRst, &result));
201         return result;
202     }
203 
GetMinByteSize() const204     size_t TextDecoder::GetMinByteSize() const
205     {
206         if (tranTool_ == nullptr) {
207             return 0;
208         }
209         size_t res = static_cast<size_t>(ucnv_getMinCharSize(tranTool_.get()));
210         return res;
211     }
212 
Reset() const213     void TextDecoder::Reset() const
214     {
215         if (tranTool_ == nullptr) {
216             return;
217         }
218         ucnv_reset(tranTool_.get());
219     }
220 
FreedMemory(UChar * & pData)221     void TextDecoder::FreedMemory(UChar *&pData)
222     {
223         if (pData != nullptr) {
224             delete[] pData;
225             pData = nullptr;
226         }
227     }
228 
SetBomFlag(const UChar * arr,const UErrorCode codeFlag,const DecodeArr decArr,size_t & rstLen,bool & bomFlag)229     void TextDecoder::SetBomFlag(const UChar *arr, const UErrorCode codeFlag, const DecodeArr decArr,
230                                  size_t &rstLen, bool &bomFlag)
231     {
232         if (arr == nullptr) {
233             return;
234         }
235         if (U_SUCCESS(codeFlag)) {
236             if (decArr.limitLen > 0) {
237                 rstLen = reinterpret_cast<uintptr_t>(decArr.target) - decArr.tarStartPos;
238                 if (rstLen > 0 && IsUnicode() && !IsIgnoreBom() && !IsBomFlag()) {
239                     bomFlag = (arr[0] == 0xFEFF) ? true : false;
240                     label_ |= static_cast<int32_t>(ConverterFlags::BOM_SEEN_FLG);
241                 }
242             }
243         }
244     }
245 
SetIgnoreBOM(const UChar * arr,size_t resultLen,bool & bomFlag)246     void TextDecoder::SetIgnoreBOM(const UChar *arr, size_t resultLen, bool &bomFlag)
247     {
248         switch (ucnv_getType(GetConverterPtr())) {
249             case UCNV_UTF8:
250             case UCNV_UTF16_BigEndian:
251             case UCNV_UTF16_LittleEndian:
252                 label_ |= static_cast<int32_t>(ConverterFlags::UNICODE_FLG);
253                 break;
254             default:
255                 break;
256         }
257         if (resultLen > 0 && IsUnicode() && IsIgnoreBom()) {
258             bomFlag = (arr[0] == 0xFEFF) ? true : false;
259         }
260         label_ |= static_cast<int32_t>(ConverterFlags::BOM_SEEN_FLG);
261     }
262 
ThrowError(napi_env env,const char * errMessage)263     napi_value TextDecoder::ThrowError(napi_env env, const char* errMessage)
264     {
265         napi_value utilError = nullptr;
266         napi_value code = nullptr;
267         uint32_t errCode = 10200019;
268         napi_create_uint32(env, errCode, &code);
269         napi_value name = nullptr;
270         std::string errName = "BusinessError";
271         napi_value msg = nullptr;
272         napi_create_string_utf8(env, errMessage, NAPI_AUTO_LENGTH, &msg);
273         napi_create_string_utf8(env, errName.c_str(), NAPI_AUTO_LENGTH, &name);
274         napi_create_error(env, nullptr, msg, &utilError);
275         napi_set_named_property(env, utilError, "code", code);
276         napi_set_named_property(env, utilError, "name", name);
277         napi_throw(env, utilError);
278         napi_value res = nullptr;
279         NAPI_CALL(env, napi_get_undefined(env, &res));
280         return res;
281     }
282 
ReplaceNull(void * data,size_t length) const283     const char* TextDecoder::ReplaceNull(void *data, size_t length) const
284     {
285         char *str = static_cast<char*>(data);
286         if (encStr_ == "utf-8") {
287             for (size_t i = 0; i < length; ++i) {
288                 if (str[i] == '\0') {
289                     str[i] = ' ';
290                 }
291             }
292         }
293         return const_cast<const char*>(str);
294     }
295 }
296