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