1 /*
2 * Copyright (c) 2021 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 "quickjs_native_string.h"
17
18 #include "securec.h"
19 #include "utils/log.h"
20
QuickJSNativeString(QuickJSNativeEngine * engine,JSValue value)21 QuickJSNativeString::QuickJSNativeString(QuickJSNativeEngine* engine, JSValue value) : QuickJSNativeValue(engine, value)
22 {
23 }
24
QuickJSNativeString(QuickJSNativeEngine * engine,JSAtom value)25 QuickJSNativeString::QuickJSNativeString(QuickJSNativeEngine* engine, JSAtom value)
26 : QuickJSNativeString(engine, JS_AtomToString(engine->GetContext(), value))
27 {
28 }
29
QuickJSNativeString(QuickJSNativeEngine * engine,const char * value,size_t length)30 QuickJSNativeString::QuickJSNativeString(QuickJSNativeEngine* engine, const char* value, size_t length)
31 : QuickJSNativeString(engine, JS_NewStringLen(engine->GetContext(), value, length))
32 {
33 }
34
QuickJSNativeString(QuickJSNativeEngine * engine,const char16_t * value,size_t length)35 QuickJSNativeString::QuickJSNativeString(QuickJSNativeEngine* engine, const char16_t* value, size_t length)
36 : QuickJSNativeString(
37 engine, JS_NewString16(engine->GetContext(), reinterpret_cast<const uint16_t*>(value), length))
38 {
39 }
40
~QuickJSNativeString()41 QuickJSNativeString::~QuickJSNativeString() {}
42
GetInterface(int interfaceId)43 void* QuickJSNativeString::GetInterface(int interfaceId)
44 {
45 return (NativeString::INTERFACE_ID == interfaceId) ? (NativeString*)this : nullptr;
46 }
47
GetCString(char * buffer,size_t size,size_t * length)48 void QuickJSNativeString::GetCString(char* buffer, size_t size, size_t* length)
49 {
50 const char* str = JS_ToCStringLen(engine_->GetContext(), length, value_);
51
52 if (str == nullptr) {
53 HILOG_ERROR("JS_ToCStringLen return value is null");
54 return;
55 }
56
57 if (buffer != nullptr && length != nullptr) {
58 int ret = strncpy_s(buffer, size, str, *length);
59 if (ret != EOK) {
60 HILOG_ERROR("strncpy_s failed");
61 }
62 }
63 JS_FreeCString(engine_->GetContext(), str);
64 }
GetCString16(char16_t * buffer,size_t size,size_t * length)65 void QuickJSNativeString::GetCString16(char16_t* buffer, size_t size, size_t* length)
66 {
67 const char* str = JS_ToCStringLen(engine_->GetContext(), length, value_);
68 if (str == nullptr) {
69 HILOG_ERROR("JS_ToCStringLen return value is null");
70 return;
71 }
72 if (length != nullptr) {
73 auto ret = Utf8ToUtf16Length(str, strlen(str));
74 if (ret == -1) {
75 HILOG_ERROR("JS_ToCStringLen length is invalid");
76 JS_FreeCString(engine_->GetContext(), str);
77 return;
78 }
79 *length = ret;
80 if (buffer != nullptr) {
81 memset_s(buffer, sizeof(char16_t) * size, 0x0, sizeof(char16_t) * size);
82 Utf8ToUtf16(str, strlen(str), buffer, size);
83 }
84 }
85 JS_FreeCString(engine_->GetContext(), str);
86 }
87
GetLength()88 size_t QuickJSNativeString::GetLength()
89 {
90 size_t length = 0;
91 GetCString(nullptr, 0, &length);
92 return length;
93 }
94
EncodeWriteUtf8(char * buffer,size_t bufferSize,int32_t * nchars)95 size_t QuickJSNativeString::EncodeWriteUtf8(char* buffer, size_t bufferSize, int32_t* nchars)
96 {
97 if (buffer == nullptr || nchars == nullptr) {
98 HILOG_ERROR("buffer is null or nchars is null");
99 return 0;
100 }
101
102 JSContext* ctx = engine_->GetContext();
103 JSValue lengthVal = JS_GetPropertyStr(ctx, value_, "length");
104 if (JS_IsException(lengthVal)) {
105 HILOG_ERROR("Failed to obtain the length");
106 return 0;
107 }
108 uint32_t len = 0;
109 if (JS_ToUint32(ctx, &len, lengthVal)) {
110 JS_FreeValue(ctx, lengthVal);
111 HILOG_ERROR("Cannot convert length to uint32_t");
112 return 0;
113 }
114
115 size_t pos = 0;
116 size_t writableSize = bufferSize;
117 uint32_t i = 0;
118 for (; i < len; i++) {
119 JSValue ch = JS_GetPropertyUint32(ctx, value_, i);
120 size_t chLen = 0;
121 const char* str = JS_ToCStringLen(ctx, &chLen, ch);
122 JS_FreeValue(ctx, ch);
123 if (chLen > writableSize) {
124 JS_FreeCString(ctx, str);
125 break;
126 }
127
128 int ret = memcpy_s((buffer + pos), writableSize, str, chLen);
129 if (ret != EOK) {
130 HILOG_ERROR("memcpy_s failed");
131 JS_FreeCString(ctx, str);
132 break;
133 }
134
135 writableSize -= chLen;
136 pos = bufferSize - writableSize;
137 JS_FreeCString(ctx, str);
138 }
139
140 *nchars = i;
141 JS_FreeValue(ctx, lengthVal);
142 return pos;
143 }
144
Utf8ToUtf16(const char * utf8Str,size_t u8len,char16_t * u16str,size_t u16len)145 char16_t* QuickJSNativeString::Utf8ToUtf16(const char* utf8Str, size_t u8len, char16_t* u16str, size_t u16len)
146 {
147 if (u16len == 0) {
148 return u16str;
149 }
150 const char* u8end = utf8Str + u8len;
151 const char* u8cur = utf8Str;
152 const char16_t* u16end = u16str + u16len - 1;
153 constexpr uint8_t offset = 10;
154 char16_t* u16cur = u16str;
155
156 while ((u8cur < u8end) && (u16cur < u16end)) {
157 size_t len = Utf8CodePointLen(*u8cur);
158 uint32_t codepoint = Utf8ToUtf32CodePoint(u8cur, len);
159 // Convert the UTF32 codepoint to one or more UTF16 codepoints
160 if (codepoint <= 0xFFFF) {
161 // Single UTF16 character
162 *u16cur++ = (char16_t)codepoint;
163 } else {
164 // Multiple UTF16 characters with surrogates
165 codepoint = codepoint - 0x10000;
166 *u16cur++ = (char16_t)((codepoint >> offset) + 0xD800);
167 if (u16cur >= u16end) {
168 // Ooops... not enough room for this surrogate pair.
169 return u16cur - 1;
170 }
171 *u16cur++ = (char16_t)((codepoint & 0x3FF) + 0xDC00);
172 }
173
174 u8cur += len;
175 }
176 return u16cur;
177 }
178
Utf8CodePointLen(uint8_t ch)179 size_t QuickJSNativeString::Utf8CodePointLen(uint8_t ch)
180 {
181 constexpr uint8_t offset = 3;
182 return ((0xe5000000 >> ((ch >> offset) & 0x1e)) & offset) + 1;
183 }
184
Utf8ToUtf32CodePoint(const char * src,size_t length)185 uint32_t QuickJSNativeString::Utf8ToUtf32CodePoint(const char* src, size_t length)
186 {
187 uint32_t unicode = 0;
188 constexpr size_t lengthSizeOne = 1;
189 constexpr size_t lengthSizeTwo = 2;
190 constexpr size_t lengthSizeThree = 3;
191 constexpr size_t lengthSizeFour = 4;
192 constexpr size_t offsetZero = 0;
193 constexpr size_t offsetOne = 1;
194 constexpr size_t offsetTwo = 2;
195 constexpr size_t offsetThree = 3;
196 switch (length) {
197 case lengthSizeOne:
198 return src[offsetZero];
199 case lengthSizeTwo:
200 unicode = src[offsetZero] & 0x1f;
201 Utf8ShiftAndMask(&unicode, src[offsetOne]);
202 return unicode;
203 case lengthSizeThree:
204 unicode = src[offsetZero] & 0x0f;
205 Utf8ShiftAndMask(&unicode, src[offsetOne]);
206 Utf8ShiftAndMask(&unicode, src[offsetTwo]);
207 return unicode;
208 case lengthSizeFour:
209 unicode = src[offsetZero] & 0x07;
210 Utf8ShiftAndMask(&unicode, src[offsetOne]);
211 Utf8ShiftAndMask(&unicode, src[offsetTwo]);
212 Utf8ShiftAndMask(&unicode, src[offsetThree]);
213 return unicode;
214 default:
215 return 0xffff;
216 }
217 }
218
Utf8ShiftAndMask(uint32_t * codePoint,const uint8_t byte)219 void QuickJSNativeString::Utf8ShiftAndMask(uint32_t* codePoint, const uint8_t byte)
220 {
221 *codePoint <<= 6;
222 *codePoint |= 0x3F & byte;
223 }
224
Utf8ToUtf16Length(const char * str8,size_t str8Len)225 int QuickJSNativeString::Utf8ToUtf16Length(const char* str8, size_t str8Len)
226 {
227 const char* str8end = str8 + str8Len;
228 int utf16len = 0;
229 while (str8 < str8end) {
230 utf16len++;
231 size_t u8charlen = Utf8CodePointLen(*str8);
232 if (str8 + u8charlen - 1 >= str8end) {
233 HILOG_ERROR("Get str16 length failed because str8 unicode is illegal!");
234 return -1;
235 }
236 uint32_t codepoint = Utf8ToUtf32CodePoint(str8, u8charlen);
237 if (codepoint > 0xFFFF) {
238 utf16len++; // this will be a surrogate pair in utf16
239 }
240 str8 += u8charlen;
241 }
242 if (str8 != str8end) {
243 HILOG_ERROR("Get str16 length failed because str8length is illegal!");
244 return -1;
245 }
246 return utf16len;
247 }
248