GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
Line | Branch | Exec | Source |
1 |
// Copyright Joyent, Inc. and other Node contributors. |
||
2 |
// |
||
3 |
// Permission is hereby granted, free of charge, to any person obtaining a |
||
4 |
// copy of this software and associated documentation files (the |
||
5 |
// "Software"), to deal in the Software without restriction, including |
||
6 |
// without limitation the rights to use, copy, modify, merge, publish, |
||
7 |
// distribute, sublicense, and/or sell copies of the Software, and to permit |
||
8 |
// persons to whom the Software is furnished to do so, subject to the |
||
9 |
// following conditions: |
||
10 |
// |
||
11 |
// The above copyright notice and this permission notice shall be included |
||
12 |
// in all copies or substantial portions of the Software. |
||
13 |
// |
||
14 |
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
||
15 |
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
||
16 |
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN |
||
17 |
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, |
||
18 |
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR |
||
19 |
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE |
||
20 |
// USE OR OTHER DEALINGS IN THE SOFTWARE. |
||
21 |
|||
22 |
#include "string_bytes.h" |
||
23 |
|||
24 |
#include "base64.h" |
||
25 |
#include "env-inl.h" |
||
26 |
#include "node_buffer.h" |
||
27 |
#include "node_errors.h" |
||
28 |
#include "util.h" |
||
29 |
|||
30 |
#include <climits> |
||
31 |
#include <cstring> // memcpy |
||
32 |
|||
33 |
#include <algorithm> |
||
34 |
#include <vector> |
||
35 |
|||
36 |
// When creating strings >= this length v8's gc spins up and consumes |
||
37 |
// most of the execution time. For these cases it's more performant to |
||
38 |
// use external string resources. |
||
39 |
#define EXTERN_APEX 0xFBEE9 |
||
40 |
|||
41 |
namespace node { |
||
42 |
|||
43 |
using v8::HandleScope; |
||
44 |
using v8::Isolate; |
||
45 |
using v8::Just; |
||
46 |
using v8::Local; |
||
47 |
using v8::Maybe; |
||
48 |
using v8::MaybeLocal; |
||
49 |
using v8::Nothing; |
||
50 |
using v8::String; |
||
51 |
using v8::Value; |
||
52 |
|||
53 |
namespace { |
||
54 |
|||
55 |
template <typename ResourceType, typename TypeName> |
||
56 |
class ExternString: public ResourceType { |
||
57 |
public: |
||
58 |
64 |
~ExternString() override { |
|
59 |
32 |
free(const_cast<TypeName*>(data_)); |
|
60 |
32 |
isolate()->AdjustAmountOfExternalAllocatedMemory(-byte_length()); |
|
61 |
✗✓✗✓ |
96 |
} |
62 |
|||
63 |
93 |
const TypeName* data() const override { |
|
64 |
93 |
return data_; |
|
65 |
} |
||
66 |
|||
67 |
204 |
size_t length() const override { |
|
68 |
204 |
return length_; |
|
69 |
} |
||
70 |
|||
71 |
64 |
int64_t byte_length() const { |
|
72 |
64 |
return length() * sizeof(*data()); |
|
73 |
} |
||
74 |
|||
75 |
10033 |
static MaybeLocal<Value> NewFromCopy(Isolate* isolate, |
|
76 |
const TypeName* data, |
||
77 |
size_t length, |
||
78 |
Local<Value>* error) { |
||
79 |
✓✓✗✓ |
10033 |
if (length == 0) |
80 |
2 |
return String::Empty(isolate); |
|
81 |
|||
82 |
✓✓✓✓ |
10031 |
if (length < EXTERN_APEX) |
83 |
10017 |
return NewSimpleFromCopy(isolate, data, length, error); |
|
84 |
|||
85 |
14 |
TypeName* new_data = node::UncheckedMalloc<TypeName>(length); |
|
86 |
✗✓✗✓ |
14 |
if (new_data == nullptr) { |
87 |
*error = node::ERR_MEMORY_ALLOCATION_FAILED(isolate); |
||
88 |
return MaybeLocal<Value>(); |
||
89 |
} |
||
90 |
14 |
memcpy(new_data, data, length * sizeof(*new_data)); |
|
91 |
|||
92 |
return ExternString<ResourceType, TypeName>::New(isolate, |
||
93 |
new_data, |
||
94 |
length, |
||
95 |
14 |
error); |
|
96 |
} |
||
97 |
|||
98 |
// uses "data" for external resource, and will be free'd on gc |
||
99 |
822238 |
static MaybeLocal<Value> New(Isolate* isolate, |
|
100 |
TypeName* data, |
||
101 |
size_t length, |
||
102 |
Local<Value>* error) { |
||
103 |
✗✓✗✓ |
822238 |
if (length == 0) |
104 |
return String::Empty(isolate); |
||
105 |
|||
106 |
✓✓✓✓ |
822238 |
if (length < EXTERN_APEX) { |
107 |
822206 |
MaybeLocal<Value> str = NewSimpleFromCopy(isolate, data, length, error); |
|
108 |
822206 |
free(data); |
|
109 |
822206 |
return str; |
|
110 |
} |
||
111 |
|||
112 |
ExternString* h_str = new ExternString<ResourceType, TypeName>(isolate, |
||
113 |
data, |
||
114 |
32 |
length); |
|
115 |
32 |
MaybeLocal<Value> str = NewExternal(isolate, h_str); |
|
116 |
32 |
isolate->AdjustAmountOfExternalAllocatedMemory(h_str->byte_length()); |
|
117 |
|||
118 |
✓✓✓✓ |
32 |
if (str.IsEmpty()) { |
119 |
✓✗✓✗ |
5 |
delete h_str; |
120 |
5 |
*error = node::ERR_STRING_TOO_LONG(isolate); |
|
121 |
5 |
return MaybeLocal<Value>(); |
|
122 |
} |
||
123 |
|||
124 |
27 |
return str.ToLocalChecked(); |
|
125 |
} |
||
126 |
|||
127 |
32 |
inline Isolate* isolate() const { return isolate_; } |
|
128 |
|||
129 |
private: |
||
130 |
32 |
ExternString(Isolate* isolate, const TypeName* data, size_t length) |
|
131 |
32 |
: isolate_(isolate), data_(data), length_(length) { } |
|
132 |
static MaybeLocal<Value> NewExternal(Isolate* isolate, |
||
133 |
ExternString* h_str); |
||
134 |
|||
135 |
// This method does not actually create ExternString instances. |
||
136 |
static MaybeLocal<Value> NewSimpleFromCopy(Isolate* isolate, |
||
137 |
const TypeName* data, |
||
138 |
size_t length, |
||
139 |
Local<Value>* error); |
||
140 |
|||
141 |
Isolate* isolate_; |
||
142 |
const TypeName* data_; |
||
143 |
size_t length_; |
||
144 |
}; |
||
145 |
|||
146 |
|||
147 |
typedef ExternString<String::ExternalOneByteStringResource, |
||
148 |
char> ExternOneByteString; |
||
149 |
typedef ExternString<String::ExternalStringResource, |
||
150 |
uint16_t> ExternTwoByteString; |
||
151 |
|||
152 |
|||
153 |
template <> |
||
154 |
27 |
MaybeLocal<Value> ExternOneByteString::NewExternal( |
|
155 |
Isolate* isolate, ExternOneByteString* h_str) { |
||
156 |
54 |
return String::NewExternalOneByte(isolate, h_str).FromMaybe(Local<Value>()); |
|
157 |
} |
||
158 |
|||
159 |
|||
160 |
template <> |
||
161 |
5 |
MaybeLocal<Value> ExternTwoByteString::NewExternal( |
|
162 |
Isolate* isolate, ExternTwoByteString* h_str) { |
||
163 |
10 |
return String::NewExternalTwoByte(isolate, h_str).FromMaybe(Local<Value>()); |
|
164 |
} |
||
165 |
|||
166 |
template <> |
||
167 |
828431 |
MaybeLocal<Value> ExternOneByteString::NewSimpleFromCopy(Isolate* isolate, |
|
168 |
const char* data, |
||
169 |
size_t length, |
||
170 |
Local<Value>* error) { |
||
171 |
MaybeLocal<String> str = |
||
172 |
String::NewFromOneByte(isolate, |
||
173 |
reinterpret_cast<const uint8_t*>(data), |
||
174 |
v8::NewStringType::kNormal, |
||
175 |
828431 |
length); |
|
176 |
✗✓ | 828431 |
if (str.IsEmpty()) { |
177 |
*error = node::ERR_STRING_TOO_LONG(isolate); |
||
178 |
return MaybeLocal<Value>(); |
||
179 |
} |
||
180 |
828431 |
return str.ToLocalChecked(); |
|
181 |
} |
||
182 |
|||
183 |
|||
184 |
template <> |
||
185 |
3792 |
MaybeLocal<Value> ExternTwoByteString::NewSimpleFromCopy(Isolate* isolate, |
|
186 |
const uint16_t* data, |
||
187 |
size_t length, |
||
188 |
Local<Value>* error) { |
||
189 |
MaybeLocal<String> str = |
||
190 |
String::NewFromTwoByte(isolate, |
||
191 |
data, |
||
192 |
v8::NewStringType::kNormal, |
||
193 |
3792 |
length); |
|
194 |
✗✓ | 3792 |
if (str.IsEmpty()) { |
195 |
*error = node::ERR_STRING_TOO_LONG(isolate); |
||
196 |
return MaybeLocal<Value>(); |
||
197 |
} |
||
198 |
3792 |
return str.ToLocalChecked(); |
|
199 |
} |
||
200 |
|||
201 |
} // anonymous namespace |
||
202 |
|||
203 |
// supports regular and URL-safe base64 |
||
204 |
const int8_t unbase64_table[256] = |
||
205 |
{ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -2, -1, -1, -2, -1, -1, |
||
206 |
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
||
207 |
-2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, 62, -1, 63, |
||
208 |
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, |
||
209 |
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, |
||
210 |
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, 63, |
||
211 |
-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, |
||
212 |
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, |
||
213 |
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
||
214 |
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
||
215 |
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
||
216 |
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
||
217 |
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
||
218 |
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
||
219 |
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
||
220 |
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 |
||
221 |
}; |
||
222 |
|||
223 |
|||
224 |
static const int8_t unhex_table[256] = |
||
225 |
{ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
||
226 |
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
||
227 |
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
||
228 |
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, |
||
229 |
-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
||
230 |
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
||
231 |
-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
||
232 |
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
||
233 |
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
||
234 |
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
||
235 |
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
||
236 |
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
||
237 |
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
||
238 |
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
||
239 |
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
||
240 |
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 |
||
241 |
}; |
||
242 |
|||
243 |
45244448 |
static inline unsigned unhex(uint8_t x) { |
|
244 |
45244448 |
return unhex_table[x]; |
|
245 |
} |
||
246 |
|||
247 |
template <typename TypeName> |
||
248 |
2345 |
static size_t hex_decode(char* buf, |
|
249 |
size_t len, |
||
250 |
const TypeName* src, |
||
251 |
const size_t srcLen) { |
||
252 |
size_t i; |
||
253 |
✓✓✓✓ ✓✓✓✗ |
22624560 |
for (i = 0; i < len && i * 2 + 1 < srcLen; ++i) { |
254 |
22622224 |
unsigned a = unhex(src[i * 2 + 0]); |
|
255 |
22622224 |
unsigned b = unhex(src[i * 2 + 1]); |
|
256 |
✓✓✗✓ ✓✗✗✓ |
22622224 |
if (!~a || !~b) |
257 |
9 |
return i; |
|
258 |
22622215 |
buf[i] = (a << 4) | b; |
|
259 |
} |
||
260 |
|||
261 |
2336 |
return i; |
|
262 |
} |
||
263 |
|||
264 |
176 |
size_t StringBytes::WriteUCS2(Isolate* isolate, |
|
265 |
char* buf, |
||
266 |
size_t buflen, |
||
267 |
Local<String> str, |
||
268 |
int flags, |
||
269 |
size_t* chars_written) { |
||
270 |
176 |
uint16_t* const dst = reinterpret_cast<uint16_t*>(buf); |
|
271 |
|||
272 |
176 |
size_t max_chars = buflen / sizeof(*dst); |
|
273 |
✓✓ | 176 |
if (max_chars == 0) { |
274 |
5 |
return 0; |
|
275 |
} |
||
276 |
|||
277 |
size_t nchars; |
||
278 |
171 |
size_t alignment = reinterpret_cast<uintptr_t>(dst) % sizeof(*dst); |
|
279 |
✓✓ | 171 |
if (alignment == 0) { |
280 |
332 |
nchars = str->Write(isolate, dst, 0, max_chars, flags); |
|
281 |
166 |
*chars_written = nchars; |
|
282 |
166 |
return nchars * sizeof(*dst); |
|
283 |
} |
||
284 |
|||
285 |
uint16_t* aligned_dst = |
||
286 |
5 |
reinterpret_cast<uint16_t*>(buf + sizeof(*dst) - alignment); |
|
287 |
✗✓ | 5 |
CHECK_EQ(reinterpret_cast<uintptr_t>(aligned_dst) % sizeof(*dst), 0); |
288 |
|||
289 |
// Write all but the last char |
||
290 |
5 |
max_chars = std::min(max_chars, static_cast<size_t>(str->Length())); |
|
291 |
✗✓ | 5 |
if (max_chars == 0) return 0; |
292 |
10 |
nchars = str->Write(isolate, aligned_dst, 0, max_chars - 1, flags); |
|
293 |
✗✓ | 5 |
CHECK_EQ(nchars, max_chars - 1); |
294 |
|||
295 |
// Shift everything to unaligned-left |
||
296 |
5 |
memmove(dst, aligned_dst, nchars * sizeof(*dst)); |
|
297 |
|||
298 |
// One more char to be written |
||
299 |
uint16_t last; |
||
300 |
✗✓ | 10 |
CHECK_EQ(str->Write(isolate, &last, nchars, 1, flags), 1); |
301 |
5 |
memcpy(buf + nchars * sizeof(*dst), &last, sizeof(last)); |
|
302 |
5 |
nchars++; |
|
303 |
|||
304 |
5 |
*chars_written = nchars; |
|
305 |
5 |
return nchars * sizeof(*dst); |
|
306 |
} |
||
307 |
|||
308 |
|||
309 |
2740281 |
size_t StringBytes::Write(Isolate* isolate, |
|
310 |
char* buf, |
||
311 |
size_t buflen, |
||
312 |
Local<Value> val, |
||
313 |
enum encoding encoding, |
||
314 |
int* chars_written) { |
||
315 |
2740281 |
HandleScope scope(isolate); |
|
316 |
size_t nbytes; |
||
317 |
int nchars; |
||
318 |
|||
319 |
✓✗ | 2740281 |
if (chars_written == nullptr) |
320 |
2740281 |
chars_written = &nchars; |
|
321 |
|||
322 |
✗✓ | 5480562 |
CHECK(val->IsString() == true); |
323 |
2740281 |
Local<String> str = val.As<String>(); |
|
324 |
|||
325 |
int flags = String::HINT_MANY_WRITES_EXPECTED | |
||
326 |
String::NO_NULL_TERMINATION | |
||
327 |
2740281 |
String::REPLACE_INVALID_UTF8; |
|
328 |
|||
329 |
✓✓✓✓ ✓✗ |
2740281 |
switch (encoding) { |
330 |
case ASCII: |
||
331 |
case LATIN1: |
||
332 |
✓✓ | 206530 |
if (str->IsExternalOneByte()) { |
333 |
1 |
auto ext = str->GetExternalOneByteStringResource(); |
|
334 |
1 |
nbytes = std::min(buflen, ext->length()); |
|
335 |
1 |
memcpy(buf, ext->data(), nbytes); |
|
336 |
} else { |
||
337 |
206529 |
uint8_t* const dst = reinterpret_cast<uint8_t*>(buf); |
|
338 |
413058 |
nbytes = str->WriteOneByte(isolate, dst, 0, buflen, flags); |
|
339 |
} |
||
340 |
206530 |
*chars_written = nbytes; |
|
341 |
206530 |
break; |
|
342 |
|||
343 |
case BUFFER: |
||
344 |
case UTF8: |
||
345 |
4251872 |
nbytes = str->WriteUtf8(isolate, buf, buflen, chars_written, flags); |
|
346 |
2125936 |
break; |
|
347 |
|||
348 |
case UCS2: { |
||
349 |
size_t nchars; |
||
350 |
|||
351 |
176 |
nbytes = WriteUCS2(isolate, buf, buflen, str, flags, &nchars); |
|
352 |
176 |
*chars_written = static_cast<int>(nchars); |
|
353 |
|||
354 |
// Node's "ucs2" encoding wants LE character data stored in |
||
355 |
// the Buffer, so we need to reorder on BE platforms. See |
||
356 |
// https://nodejs.org/api/buffer.html regarding Node's "ucs2" |
||
357 |
// encoding specification |
||
358 |
✗✓ | 176 |
if (IsBigEndian()) |
359 |
SwapBytes16(buf, nbytes); |
||
360 |
|||
361 |
176 |
break; |
|
362 |
} |
||
363 |
|||
364 |
case BASE64: |
||
365 |
✓✓ | 405294 |
if (str->IsExternalOneByte()) { |
366 |
4 |
auto ext = str->GetExternalOneByteStringResource(); |
|
367 |
4 |
nbytes = base64_decode(buf, buflen, ext->data(), ext->length()); |
|
368 |
} else { |
||
369 |
405290 |
String::Value value(isolate, str); |
|
370 |
405290 |
nbytes = base64_decode(buf, buflen, *value, value.length()); |
|
371 |
} |
||
372 |
405294 |
*chars_written = nbytes; |
|
373 |
405294 |
break; |
|
374 |
|||
375 |
case HEX: |
||
376 |
✓✓ | 2345 |
if (str->IsExternalOneByte()) { |
377 |
22 |
auto ext = str->GetExternalOneByteStringResource(); |
|
378 |
22 |
nbytes = hex_decode(buf, buflen, ext->data(), ext->length()); |
|
379 |
} else { |
||
380 |
2323 |
String::Value value(isolate, str); |
|
381 |
2323 |
nbytes = hex_decode(buf, buflen, *value, value.length()); |
|
382 |
} |
||
383 |
2345 |
*chars_written = nbytes; |
|
384 |
2345 |
break; |
|
385 |
|||
386 |
default: |
||
387 |
CHECK(0 && "unknown encoding"); |
||
388 |
break; |
||
389 |
} |
||
390 |
|||
391 |
2740281 |
return nbytes; |
|
392 |
} |
||
393 |
|||
394 |
|||
395 |
// Quick and dirty size calculation |
||
396 |
// Will always be at least big enough, but may have some extra |
||
397 |
// UTF8 can be as much as 3x the size, Base64 can have 1-2 extra bytes |
||
398 |
8514048 |
Maybe<size_t> StringBytes::StorageSize(Isolate* isolate, |
|
399 |
Local<Value> val, |
||
400 |
enum encoding encoding) { |
||
401 |
8514048 |
HandleScope scope(isolate); |
|
402 |
8514049 |
size_t data_size = 0; |
|
403 |
8514049 |
bool is_buffer = Buffer::HasInstance(val); |
|
404 |
|||
405 |
✗✓✗✗ ✗✗ |
8514049 |
if (is_buffer && (encoding == BUFFER || encoding == LATIN1)) { |
406 |
return Just(Buffer::Length(val)); |
||
407 |
} |
||
408 |
|||
409 |
Local<String> str; |
||
410 |
✗✓ | 25542140 |
if (!val->ToString(isolate->GetCurrentContext()).ToLocal(&str)) |
411 |
return Nothing<size_t>(); |
||
412 |
|||
413 |
✓✓✓✓ ✓✗ |
8514046 |
switch (encoding) { |
414 |
case ASCII: |
||
415 |
case LATIN1: |
||
416 |
204614 |
data_size = str->Length(); |
|
417 |
204614 |
break; |
|
418 |
|||
419 |
case BUFFER: |
||
420 |
case UTF8: |
||
421 |
// A single UCS2 codepoint never takes up more than 3 utf8 bytes. |
||
422 |
// It is an exercise for the caller to decide when a string is |
||
423 |
// long enough to justify calling Size() instead of StorageSize() |
||
424 |
8308263 |
data_size = 3 * str->Length(); |
|
425 |
8308264 |
break; |
|
426 |
|||
427 |
case UCS2: |
||
428 |
6 |
data_size = str->Length() * sizeof(uint16_t); |
|
429 |
6 |
break; |
|
430 |
|||
431 |
case BASE64: |
||
432 |
10 |
data_size = base64_decoded_size_fast(str->Length()); |
|
433 |
10 |
break; |
|
434 |
|||
435 |
case HEX: |
||
436 |
✗✓✗✓ |
1153 |
CHECK(str->Length() % 2 == 0 && "invalid hex string length"); |
437 |
1153 |
data_size = str->Length() / 2; |
|
438 |
1153 |
break; |
|
439 |
|||
440 |
default: |
||
441 |
CHECK(0 && "unknown encoding"); |
||
442 |
break; |
||
443 |
} |
||
444 |
|||
445 |
8514047 |
return Just(data_size); |
|
446 |
} |
||
447 |
|||
448 |
1096 |
Maybe<size_t> StringBytes::Size(Isolate* isolate, |
|
449 |
Local<Value> val, |
||
450 |
enum encoding encoding) { |
||
451 |
1096 |
HandleScope scope(isolate); |
|
452 |
|||
453 |
✗✓✗✗ ✗✗✗✓ |
1096 |
if (Buffer::HasInstance(val) && (encoding == BUFFER || encoding == LATIN1)) |
454 |
return Just(Buffer::Length(val)); |
||
455 |
|||
456 |
Local<String> str; |
||
457 |
✗✓ | 3288 |
if (!val->ToString(isolate->GetCurrentContext()).ToLocal(&str)) |
458 |
return Nothing<size_t>(); |
||
459 |
|||
460 |
✓✓✓✓ ✓✗ |
1096 |
switch (encoding) { |
461 |
case ASCII: |
||
462 |
case LATIN1: |
||
463 |
17 |
return Just<size_t>(str->Length()); |
|
464 |
|||
465 |
case BUFFER: |
||
466 |
case UTF8: |
||
467 |
953 |
return Just<size_t>(str->Utf8Length(isolate)); |
|
468 |
|||
469 |
case UCS2: |
||
470 |
97 |
return Just(str->Length() * sizeof(uint16_t)); |
|
471 |
|||
472 |
case BASE64: { |
||
473 |
5 |
String::Value value(isolate, str); |
|
474 |
5 |
return Just(base64_decoded_size(*value, value.length())); |
|
475 |
} |
||
476 |
|||
477 |
case HEX: |
||
478 |
24 |
return Just<size_t>(str->Length() / 2); |
|
479 |
} |
||
480 |
|||
481 |
UNREACHABLE(); |
||
482 |
} |
||
483 |
|||
484 |
|||
485 |
|||
486 |
|||
487 |
4666 |
static bool contains_non_ascii_slow(const char* buf, size_t len) { |
|
488 |
✓✓ | 22123 |
for (size_t i = 0; i < len; ++i) { |
489 |
✓✓ | 17623 |
if (buf[i] & 0x80) |
490 |
166 |
return true; |
|
491 |
} |
||
492 |
4500 |
return false; |
|
493 |
} |
||
494 |
|||
495 |
|||
496 |
5762 |
static bool contains_non_ascii(const char* src, size_t len) { |
|
497 |
✓✓ | 5762 |
if (len < 16) { |
498 |
4560 |
return contains_non_ascii_slow(src, len); |
|
499 |
} |
||
500 |
|||
501 |
1202 |
const unsigned bytes_per_word = sizeof(uintptr_t); |
|
502 |
1202 |
const unsigned align_mask = bytes_per_word - 1; |
|
503 |
1202 |
const unsigned unaligned = reinterpret_cast<uintptr_t>(src) & align_mask; |
|
504 |
|||
505 |
✓✓ | 1202 |
if (unaligned > 0) { |
506 |
51 |
const unsigned n = bytes_per_word - unaligned; |
|
507 |
✓✓ | 51 |
if (contains_non_ascii_slow(src, n)) |
508 |
17 |
return true; |
|
509 |
34 |
src += n; |
|
510 |
34 |
len -= n; |
|
511 |
} |
||
512 |
|||
513 |
|||
514 |
#if defined(_WIN64) || defined(_LP64) |
||
515 |
1185 |
const uintptr_t mask = 0x8080808080808080ll; |
|
516 |
#else |
||
517 |
const uintptr_t mask = 0x80808080l; |
||
518 |
#endif |
||
519 |
|||
520 |
1185 |
const uintptr_t* srcw = reinterpret_cast<const uintptr_t*>(src); |
|
521 |
|||
522 |
✓✓ | 139600666 |
for (size_t i = 0, n = len / bytes_per_word; i < n; ++i) { |
523 |
✓✓ | 139599937 |
if (srcw[i] & mask) |
524 |
456 |
return true; |
|
525 |
} |
||
526 |
|||
527 |
729 |
const unsigned remainder = len & align_mask; |
|
528 |
✓✓ | 729 |
if (remainder > 0) { |
529 |
55 |
const size_t offset = len - remainder; |
|
530 |
✗✓ | 55 |
if (contains_non_ascii_slow(src + offset, remainder)) |
531 |
return true; |
||
532 |
} |
||
533 |
|||
534 |
729 |
return false; |
|
535 |
} |
||
536 |
|||
537 |
|||
538 |
571 |
static void force_ascii_slow(const char* src, char* dst, size_t len) { |
|
539 |
✓✓ | 5607 |
for (size_t i = 0; i < len; ++i) { |
540 |
5036 |
dst[i] = src[i] & 0x7f; |
|
541 |
} |
||
542 |
571 |
} |
|
543 |
|||
544 |
|||
545 |
622 |
static void force_ascii(const char* src, char* dst, size_t len) { |
|
546 |
✓✓ | 622 |
if (len < 16) { |
547 |
149 |
force_ascii_slow(src, dst, len); |
|
548 |
149 |
return; |
|
549 |
} |
||
550 |
|||
551 |
473 |
const unsigned bytes_per_word = sizeof(uintptr_t); |
|
552 |
473 |
const unsigned align_mask = bytes_per_word - 1; |
|
553 |
473 |
const unsigned src_unalign = reinterpret_cast<uintptr_t>(src) & align_mask; |
|
554 |
473 |
const unsigned dst_unalign = reinterpret_cast<uintptr_t>(dst) & align_mask; |
|
555 |
|||
556 |
✓✓ | 473 |
if (src_unalign > 0) { |
557 |
✗✓ | 50 |
if (src_unalign == dst_unalign) { |
558 |
const unsigned unalign = bytes_per_word - src_unalign; |
||
559 |
force_ascii_slow(src, dst, unalign); |
||
560 |
src += unalign; |
||
561 |
dst += unalign; |
||
562 |
len -= src_unalign; |
||
563 |
} else { |
||
564 |
50 |
force_ascii_slow(src, dst, len); |
|
565 |
50 |
return; |
|
566 |
} |
||
567 |
} |
||
568 |
|||
569 |
#if defined(_WIN64) || defined(_LP64) |
||
570 |
423 |
const uintptr_t mask = ~0x8080808080808080ll; |
|
571 |
#else |
||
572 |
const uintptr_t mask = ~0x80808080l; |
||
573 |
#endif |
||
574 |
|||
575 |
423 |
const uintptr_t* srcw = reinterpret_cast<const uintptr_t*>(src); |
|
576 |
423 |
uintptr_t* dstw = reinterpret_cast<uintptr_t*>(dst); |
|
577 |
|||
578 |
✓✓ | 3036 |
for (size_t i = 0, n = len / bytes_per_word; i < n; ++i) { |
579 |
2613 |
dstw[i] = srcw[i] & mask; |
|
580 |
} |
||
581 |
|||
582 |
423 |
const unsigned remainder = len & align_mask; |
|
583 |
✓✓ | 423 |
if (remainder > 0) { |
584 |
372 |
const size_t offset = len - remainder; |
|
585 |
372 |
force_ascii_slow(src + offset, dst + offset, remainder); |
|
586 |
} |
||
587 |
} |
||
588 |
|||
589 |
|||
590 |
815702 |
static size_t hex_encode(const char* src, size_t slen, char* dst, size_t dlen) { |
|
591 |
// We know how much we'll write, just make sure that there's space. |
||
592 |
✗✓ | 815702 |
CHECK(dlen >= slen * 2 && |
593 |
"not enough space provided for hex encode"); |
||
594 |
|||
595 |
815702 |
dlen = slen * 2; |
|
596 |
✓✓ | 1114949593 |
for (uint32_t i = 0, k = 0; k < dlen; i += 1, k += 2) { |
597 |
static const char hex[] = "0123456789abcdef"; |
||
598 |
1114133891 |
uint8_t val = static_cast<uint8_t>(src[i]); |
|
599 |
1114133891 |
dst[k + 0] = hex[val >> 4]; |
|
600 |
1114133891 |
dst[k + 1] = hex[val & 15]; |
|
601 |
} |
||
602 |
|||
603 |
815702 |
return dlen; |
|
604 |
} |
||
605 |
|||
606 |
|||
607 |
#define CHECK_BUFLEN_IN_RANGE(len) \ |
||
608 |
do { \ |
||
609 |
if ((len) > Buffer::kMaxLength) { \ |
||
610 |
*error = node::ERR_BUFFER_TOO_LARGE(isolate); \ |
||
611 |
return MaybeLocal<Value>(); \ |
||
612 |
} \ |
||
613 |
} while (0) |
||
614 |
|||
615 |
|||
616 |
1630177 |
MaybeLocal<Value> StringBytes::Encode(Isolate* isolate, |
|
617 |
const char* buf, |
||
618 |
size_t buflen, |
||
619 |
enum encoding encoding, |
||
620 |
Local<Value>* error) { |
||
621 |
✗✓ | 1630177 |
CHECK_BUFLEN_IN_RANGE(buflen); |
622 |
|||
623 |
✓✓✓✗ |
1630177 |
if (!buflen && encoding != BUFFER) { |
624 |
180 |
return String::Empty(isolate); |
|
625 |
} |
||
626 |
|||
627 |
MaybeLocal<String> val; |
||
628 |
|||
629 |
✓✓✓✓ ✓✓✓✗ |
1629997 |
switch (encoding) { |
630 |
case BUFFER: |
||
631 |
{ |
||
632 |
✗✓ | 5220 |
if (buflen > node::Buffer::kMaxLength) { |
633 |
*error = node::ERR_BUFFER_TOO_LARGE(isolate); |
||
634 |
return MaybeLocal<Value>(); |
||
635 |
} |
||
636 |
5220 |
auto maybe_buf = Buffer::Copy(isolate, buf, buflen); |
|
637 |
✗✓ | 5220 |
if (maybe_buf.IsEmpty()) { |
638 |
*error = node::ERR_MEMORY_ALLOCATION_FAILED(isolate); |
||
639 |
return MaybeLocal<Value>(); |
||
640 |
} |
||
641 |
5220 |
return maybe_buf.ToLocalChecked(); |
|
642 |
} |
||
643 |
|||
644 |
case ASCII: |
||
645 |
✓✓ | 5762 |
if (contains_non_ascii(buf, buflen)) { |
646 |
622 |
char* out = node::UncheckedMalloc(buflen); |
|
647 |
✗✓ | 622 |
if (out == nullptr) { |
648 |
*error = node::ERR_MEMORY_ALLOCATION_FAILED(isolate); |
||
649 |
return MaybeLocal<Value>(); |
||
650 |
} |
||
651 |
622 |
force_ascii(buf, out, buflen); |
|
652 |
622 |
return ExternOneByteString::New(isolate, out, buflen, error); |
|
653 |
} else { |
||
654 |
5140 |
return ExternOneByteString::NewFromCopy(isolate, buf, buflen, error); |
|
655 |
} |
||
656 |
|||
657 |
case UTF8: |
||
658 |
val = String::NewFromUtf8(isolate, |
||
659 |
buf, |
||
660 |
v8::NewStringType::kNormal, |
||
661 |
792520 |
buflen); |
|
662 |
✓✓ | 792520 |
if (val.IsEmpty()) { |
663 |
3 |
*error = node::ERR_STRING_TOO_LONG(isolate); |
|
664 |
3 |
return MaybeLocal<Value>(); |
|
665 |
} |
||
666 |
792517 |
return val.ToLocalChecked(); |
|
667 |
|||
668 |
case LATIN1: |
||
669 |
1275 |
return ExternOneByteString::NewFromCopy(isolate, buf, buflen, error); |
|
670 |
|||
671 |
case BASE64: { |
||
672 |
5719 |
size_t dlen = base64_encoded_size(buflen); |
|
673 |
5719 |
char* dst = node::UncheckedMalloc(dlen); |
|
674 |
✗✓ | 5719 |
if (dst == nullptr) { |
675 |
*error = node::ERR_MEMORY_ALLOCATION_FAILED(isolate); |
||
676 |
return MaybeLocal<Value>(); |
||
677 |
} |
||
678 |
|||
679 |
5719 |
size_t written = base64_encode(buf, buflen, dst, dlen); |
|
680 |
✗✓ | 5719 |
CHECK_EQ(written, dlen); |
681 |
|||
682 |
5719 |
return ExternOneByteString::New(isolate, dst, dlen, error); |
|
683 |
} |
||
684 |
|||
685 |
case HEX: { |
||
686 |
815702 |
size_t dlen = buflen * 2; |
|
687 |
815702 |
char* dst = node::UncheckedMalloc(dlen); |
|
688 |
✗✓ | 815702 |
if (dst == nullptr) { |
689 |
*error = node::ERR_MEMORY_ALLOCATION_FAILED(isolate); |
||
690 |
return MaybeLocal<Value>(); |
||
691 |
} |
||
692 |
815702 |
size_t written = hex_encode(buf, buflen, dst, dlen); |
|
693 |
✗✓ | 815702 |
CHECK_EQ(written, dlen); |
694 |
|||
695 |
815702 |
return ExternOneByteString::New(isolate, dst, dlen, error); |
|
696 |
} |
||
697 |
|||
698 |
case UCS2: { |
||
699 |
✗✓ | 3799 |
if (IsBigEndian()) { |
700 |
uint16_t* dst = node::UncheckedMalloc<uint16_t>(buflen / 2); |
||
701 |
if (dst == nullptr) { |
||
702 |
*error = node::ERR_MEMORY_ALLOCATION_FAILED(isolate); |
||
703 |
return MaybeLocal<Value>(); |
||
704 |
} |
||
705 |
for (size_t i = 0, k = 0; k < buflen / 2; i += 2, k += 1) { |
||
706 |
// The input is in *little endian*, because that's what Node.js |
||
707 |
// expects, so the high byte comes after the low byte. |
||
708 |
const uint8_t hi = static_cast<uint8_t>(buf[i + 1]); |
||
709 |
const uint8_t lo = static_cast<uint8_t>(buf[i + 0]); |
||
710 |
dst[k] = static_cast<uint16_t>(hi) << 8 | lo; |
||
711 |
} |
||
712 |
return ExternTwoByteString::New(isolate, dst, buflen / 2, error); |
||
713 |
} |
||
714 |
✓✓ | 3799 |
if (reinterpret_cast<uintptr_t>(buf) % 2 != 0) { |
715 |
// Unaligned data still means we can't directly pass it to V8. |
||
716 |
181 |
char* dst = node::UncheckedMalloc(buflen); |
|
717 |
✗✓ | 181 |
if (dst == nullptr) { |
718 |
*error = node::ERR_MEMORY_ALLOCATION_FAILED(isolate); |
||
719 |
return MaybeLocal<Value>(); |
||
720 |
} |
||
721 |
181 |
memcpy(dst, buf, buflen); |
|
722 |
return ExternTwoByteString::New( |
||
723 |
181 |
isolate, reinterpret_cast<uint16_t*>(dst), buflen / 2, error); |
|
724 |
} |
||
725 |
return ExternTwoByteString::NewFromCopy( |
||
726 |
3618 |
isolate, reinterpret_cast<const uint16_t*>(buf), buflen / 2, error); |
|
727 |
} |
||
728 |
|||
729 |
default: |
||
730 |
CHECK(0 && "unknown encoding"); |
||
731 |
break; |
||
732 |
} |
||
733 |
|||
734 |
UNREACHABLE(); |
||
735 |
} |
||
736 |
|||
737 |
|||
738 |
MaybeLocal<Value> StringBytes::Encode(Isolate* isolate, |
||
739 |
const uint16_t* buf, |
||
740 |
size_t buflen, |
||
741 |
Local<Value>* error) { |
||
742 |
CHECK_BUFLEN_IN_RANGE(buflen); |
||
743 |
|||
744 |
// Node's "ucs2" encoding expects LE character data inside a |
||
745 |
// Buffer, so we need to reorder on BE platforms. See |
||
746 |
// https://nodejs.org/api/buffer.html regarding Node's "ucs2" |
||
747 |
// encoding specification |
||
748 |
if (IsBigEndian()) { |
||
749 |
uint16_t* dst = node::UncheckedMalloc<uint16_t>(buflen); |
||
750 |
if (dst == nullptr) { |
||
751 |
*error = node::ERR_MEMORY_ALLOCATION_FAILED(isolate); |
||
752 |
return MaybeLocal<Value>(); |
||
753 |
} |
||
754 |
size_t nbytes = buflen * sizeof(uint16_t); |
||
755 |
memcpy(dst, buf, nbytes); |
||
756 |
SwapBytes16(reinterpret_cast<char*>(dst), nbytes); |
||
757 |
return ExternTwoByteString::New(isolate, dst, buflen, error); |
||
758 |
} else { |
||
759 |
return ExternTwoByteString::NewFromCopy(isolate, buf, buflen, error); |
||
760 |
} |
||
761 |
} |
||
762 |
|||
763 |
679806 |
MaybeLocal<Value> StringBytes::Encode(Isolate* isolate, |
|
764 |
const char* buf, |
||
765 |
enum encoding encoding, |
||
766 |
Local<Value>* error) { |
||
767 |
679806 |
const size_t len = strlen(buf); |
|
768 |
679806 |
return Encode(isolate, buf, len, encoding, error); |
|
769 |
} |
||
770 |
|||
771 |
} // namespace node |
Generated by: GCOVR (Version 3.4) |