File: | out/../src/string_bytes.cc |
Warning: | line 782, column 5 Potential leak of memory pointed to by 'dst' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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_APEX0xFBEE9 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 | ~ExternString() override { | |||
58 | free(const_cast<TypeName*>(data_)); | |||
59 | isolate()->AdjustAmountOfExternalAllocatedMemory(-byte_length()); | |||
60 | } | |||
61 | ||||
62 | const TypeName* data() const override { | |||
63 | return data_; | |||
64 | } | |||
65 | ||||
66 | size_t length() const override { | |||
67 | return length_; | |||
68 | } | |||
69 | ||||
70 | int64_t byte_length() const { | |||
71 | return length() * sizeof(*data()); | |||
72 | } | |||
73 | ||||
74 | static MaybeLocal<Value> NewFromCopy(Isolate* isolate, | |||
75 | const TypeName* data, | |||
76 | size_t length, | |||
77 | Local<Value>* error) { | |||
78 | if (length == 0) | |||
79 | return String::Empty(isolate); | |||
80 | ||||
81 | if (length < EXTERN_APEX0xFBEE9) | |||
82 | return NewSimpleFromCopy(isolate, data, length, error); | |||
83 | ||||
84 | TypeName* new_data = node::UncheckedMalloc<TypeName>(length); | |||
85 | if (new_data == nullptr) { | |||
86 | *error = node::ERR_MEMORY_ALLOCATION_FAILED(isolate); | |||
87 | return MaybeLocal<Value>(); | |||
88 | } | |||
89 | memcpy(new_data, data, length * sizeof(*new_data)); | |||
90 | ||||
91 | return ExternString<ResourceType, TypeName>::New(isolate, | |||
92 | new_data, | |||
93 | length, | |||
94 | error); | |||
95 | } | |||
96 | ||||
97 | // uses "data" for external resource, and will be free'd on gc | |||
98 | static MaybeLocal<Value> New(Isolate* isolate, | |||
99 | TypeName* data, | |||
100 | size_t length, | |||
101 | Local<Value>* error) { | |||
102 | if (length == 0) | |||
103 | return String::Empty(isolate); | |||
104 | ||||
105 | if (length < EXTERN_APEX0xFBEE9) { | |||
106 | MaybeLocal<Value> str = NewSimpleFromCopy(isolate, data, length, error); | |||
107 | free(data); | |||
108 | return str; | |||
109 | } | |||
110 | ||||
111 | ExternString* h_str = new ExternString<ResourceType, TypeName>(isolate, | |||
112 | data, | |||
113 | length); | |||
114 | MaybeLocal<Value> str = NewExternal(isolate, h_str); | |||
115 | isolate->AdjustAmountOfExternalAllocatedMemory(h_str->byte_length()); | |||
116 | ||||
117 | if (str.IsEmpty()) { | |||
118 | delete h_str; | |||
119 | *error = node::ERR_STRING_TOO_LONG(isolate); | |||
120 | return MaybeLocal<Value>(); | |||
121 | } | |||
122 | ||||
123 | return str.ToLocalChecked(); | |||
124 | } | |||
125 | ||||
126 | inline Isolate* isolate() const { return isolate_; } | |||
127 | ||||
128 | private: | |||
129 | ExternString(Isolate* isolate, const TypeName* data, size_t length) | |||
130 | : 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 | MaybeLocal<Value> ExternOneByteString::NewExternal( | |||
154 | Isolate* isolate, ExternOneByteString* h_str) { | |||
155 | return String::NewExternalOneByte(isolate, h_str).FromMaybe(Local<Value>()); | |||
156 | } | |||
157 | ||||
158 | ||||
159 | template <> | |||
160 | MaybeLocal<Value> ExternTwoByteString::NewExternal( | |||
161 | Isolate* isolate, ExternTwoByteString* h_str) { | |||
162 | return String::NewExternalTwoByte(isolate, h_str).FromMaybe(Local<Value>()); | |||
163 | } | |||
164 | ||||
165 | template <> | |||
166 | 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 | length); | |||
175 | if (str.IsEmpty()) { | |||
176 | *error = node::ERR_STRING_TOO_LONG(isolate); | |||
177 | return MaybeLocal<Value>(); | |||
178 | } | |||
179 | return str.ToLocalChecked(); | |||
180 | } | |||
181 | ||||
182 | ||||
183 | template <> | |||
184 | 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 | length); | |||
193 | if (str.IsEmpty()) { | |||
194 | *error = node::ERR_STRING_TOO_LONG(isolate); | |||
195 | return MaybeLocal<Value>(); | |||
196 | } | |||
197 | 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 | static inline unsigned unhex(uint8_t x) { | |||
243 | return unhex_table[x]; | |||
244 | } | |||
245 | ||||
246 | template <typename TypeName> | |||
247 | 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 | for (i = 0; i < len && i * 2 + 1 < srcLen; ++i) { | |||
253 | unsigned a = unhex(static_cast<uint8_t>(src[i * 2 + 0])); | |||
254 | unsigned b = unhex(static_cast<uint8_t>(src[i * 2 + 1])); | |||
255 | if (!~a || !~b) | |||
256 | return i; | |||
257 | buf[i] = (a << 4) | b; | |||
258 | } | |||
259 | ||||
260 | return i; | |||
261 | } | |||
262 | ||||
263 | size_t StringBytes::WriteUCS2(Isolate* isolate, | |||
264 | char* buf, | |||
265 | size_t buflen, | |||
266 | Local<String> str, | |||
267 | int flags, | |||
268 | size_t* chars_written) { | |||
269 | uint16_t* const dst = reinterpret_cast<uint16_t*>(buf); | |||
270 | ||||
271 | size_t max_chars = buflen / sizeof(*dst); | |||
272 | if (max_chars == 0) { | |||
273 | return 0; | |||
274 | } | |||
275 | ||||
276 | uint16_t* const aligned_dst = AlignUp(dst, sizeof(*dst)); | |||
277 | size_t nchars; | |||
278 | if (aligned_dst == dst) { | |||
279 | nchars = str->Write(isolate, dst, 0, max_chars, flags); | |||
280 | *chars_written = nchars; | |||
281 | return nchars * sizeof(*dst); | |||
282 | } | |||
283 | ||||
284 | CHECK_EQ(reinterpret_cast<uintptr_t>(aligned_dst) % sizeof(*dst), 0)do { if (__builtin_expect(!!(!((reinterpret_cast<uintptr_t >(aligned_dst) % sizeof(*dst)) == (0))), 0)) { do { static const node::AssertionInfo args = { "../src/string_bytes.cc" ":" "284", "(reinterpret_cast<uintptr_t>(aligned_dst) % sizeof(*dst)) == (0)" , __PRETTY_FUNCTION__ }; node::Assert(args); } while (0); } } while (0); | |||
285 | ||||
286 | // Write all but the last char | |||
287 | max_chars = std::min(max_chars, static_cast<size_t>(str->Length())); | |||
288 | if (max_chars == 0) return 0; | |||
289 | nchars = str->Write(isolate, aligned_dst, 0, max_chars - 1, flags); | |||
290 | CHECK_EQ(nchars, max_chars - 1)do { if (__builtin_expect(!!(!((nchars) == (max_chars - 1))), 0)) { do { static const node::AssertionInfo args = { "../src/string_bytes.cc" ":" "290", "(nchars) == (max_chars - 1)", __PRETTY_FUNCTION__ }; node::Assert(args); } while (0); } } while (0); | |||
291 | ||||
292 | // Shift everything to unaligned-left | |||
293 | memmove(dst, aligned_dst, nchars * sizeof(*dst)); | |||
294 | ||||
295 | // One more char to be written | |||
296 | uint16_t last; | |||
297 | CHECK_EQ(str->Write(isolate, &last, nchars, 1, flags), 1)do { if (__builtin_expect(!!(!((str->Write(isolate, &last , nchars, 1, flags)) == (1))), 0)) { do { static const node:: AssertionInfo args = { "../src/string_bytes.cc" ":" "297", "(str->Write(isolate, &last, nchars, 1, flags)) == (1)" , __PRETTY_FUNCTION__ }; node::Assert(args); } while (0); } } while (0); | |||
298 | memcpy(buf + nchars * sizeof(*dst), &last, sizeof(last)); | |||
299 | nchars++; | |||
300 | ||||
301 | *chars_written = nchars; | |||
302 | return nchars * sizeof(*dst); | |||
303 | } | |||
304 | ||||
305 | ||||
306 | size_t StringBytes::Write(Isolate* isolate, | |||
307 | char* buf, | |||
308 | size_t buflen, | |||
309 | Local<Value> val, | |||
310 | enum encoding encoding, | |||
311 | int* chars_written) { | |||
312 | HandleScope scope(isolate); | |||
313 | size_t nbytes; | |||
314 | int nchars; | |||
315 | ||||
316 | if (chars_written == nullptr) | |||
317 | chars_written = &nchars; | |||
318 | ||||
319 | CHECK(val->IsString() == true)do { if (__builtin_expect(!!(!(val->IsString() == true)), 0 )) { do { static const node::AssertionInfo args = { "../src/string_bytes.cc" ":" "319", "val->IsString() == true", __PRETTY_FUNCTION__ }; node::Assert(args); } while (0); } } while (0); | |||
320 | Local<String> str = val.As<String>(); | |||
321 | ||||
322 | int flags = String::HINT_MANY_WRITES_EXPECTED | | |||
323 | String::NO_NULL_TERMINATION | | |||
324 | String::REPLACE_INVALID_UTF8; | |||
325 | ||||
326 | switch (encoding) { | |||
327 | case ASCII: | |||
328 | case LATIN1: | |||
329 | if (str->IsExternalOneByte()) { | |||
330 | auto ext = str->GetExternalOneByteStringResource(); | |||
331 | nbytes = std::min(buflen, ext->length()); | |||
332 | memcpy(buf, ext->data(), nbytes); | |||
333 | } else { | |||
334 | uint8_t* const dst = reinterpret_cast<uint8_t*>(buf); | |||
335 | nbytes = str->WriteOneByte(isolate, dst, 0, buflen, flags); | |||
336 | } | |||
337 | *chars_written = nbytes; | |||
338 | break; | |||
339 | ||||
340 | case BUFFER: | |||
341 | case UTF8: | |||
342 | nbytes = str->WriteUtf8(isolate, buf, buflen, chars_written, flags); | |||
343 | break; | |||
344 | ||||
345 | case UCS2: { | |||
346 | size_t nchars; | |||
347 | ||||
348 | nbytes = WriteUCS2(isolate, buf, buflen, str, flags, &nchars); | |||
349 | *chars_written = static_cast<int>(nchars); | |||
350 | ||||
351 | // Node's "ucs2" encoding wants LE character data stored in | |||
352 | // the Buffer, so we need to reorder on BE platforms. See | |||
353 | // https://nodejs.org/api/buffer.html regarding Node's "ucs2" | |||
354 | // encoding specification | |||
355 | if (IsBigEndian()) | |||
356 | SwapBytes16(buf, nbytes); | |||
357 | ||||
358 | break; | |||
359 | } | |||
360 | ||||
361 | case BASE64URL: | |||
362 | // Fall through | |||
363 | case BASE64: | |||
364 | if (str->IsExternalOneByte()) { | |||
365 | auto ext = str->GetExternalOneByteStringResource(); | |||
366 | nbytes = base64_decode(buf, buflen, ext->data(), ext->length()); | |||
367 | } else { | |||
368 | String::Value value(isolate, str); | |||
369 | nbytes = base64_decode(buf, buflen, *value, value.length()); | |||
370 | } | |||
371 | *chars_written = nbytes; | |||
372 | break; | |||
373 | ||||
374 | case HEX: | |||
375 | if (str->IsExternalOneByte()) { | |||
376 | auto ext = str->GetExternalOneByteStringResource(); | |||
377 | nbytes = hex_decode(buf, buflen, ext->data(), ext->length()); | |||
378 | } else { | |||
379 | String::Value value(isolate, str); | |||
380 | nbytes = hex_decode(buf, buflen, *value, value.length()); | |||
381 | } | |||
382 | *chars_written = nbytes; | |||
383 | break; | |||
384 | ||||
385 | default: | |||
386 | CHECK(0 && "unknown encoding")do { if (__builtin_expect(!!(!(0 && "unknown encoding" )), 0)) { do { static const node::AssertionInfo args = { "../src/string_bytes.cc" ":" "386", "0 && \"unknown encoding\"", __PRETTY_FUNCTION__ }; node::Assert(args); } while (0); } } while (0); | |||
387 | break; | |||
388 | } | |||
389 | ||||
390 | return nbytes; | |||
391 | } | |||
392 | ||||
393 | ||||
394 | // Quick and dirty size calculation | |||
395 | // Will always be at least big enough, but may have some extra | |||
396 | // UTF8 can be as much as 3x the size, Base64 can have 1-2 extra bytes | |||
397 | Maybe<size_t> StringBytes::StorageSize(Isolate* isolate, | |||
398 | Local<Value> val, | |||
399 | enum encoding encoding) { | |||
400 | HandleScope scope(isolate); | |||
401 | size_t data_size = 0; | |||
402 | bool is_buffer = Buffer::HasInstance(val); | |||
403 | ||||
404 | if (is_buffer && (encoding == BUFFER || encoding == LATIN1)) { | |||
405 | return Just(Buffer::Length(val)); | |||
406 | } | |||
407 | ||||
408 | Local<String> str; | |||
409 | if (!val->ToString(isolate->GetCurrentContext()).ToLocal(&str)) | |||
410 | return Nothing<size_t>(); | |||
411 | ||||
412 | switch (encoding) { | |||
413 | case ASCII: | |||
414 | case LATIN1: | |||
415 | data_size = str->Length(); | |||
416 | break; | |||
417 | ||||
418 | case BUFFER: | |||
419 | case UTF8: | |||
420 | // A single UCS2 codepoint never takes up more than 3 utf8 bytes. | |||
421 | // It is an exercise for the caller to decide when a string is | |||
422 | // long enough to justify calling Size() instead of StorageSize() | |||
423 | data_size = 3 * str->Length(); | |||
424 | break; | |||
425 | ||||
426 | case UCS2: | |||
427 | data_size = str->Length() * sizeof(uint16_t); | |||
428 | break; | |||
429 | ||||
430 | case BASE64URL: | |||
431 | // Fall through | |||
432 | case BASE64: | |||
433 | data_size = base64_decoded_size_fast(str->Length()); | |||
434 | break; | |||
435 | ||||
436 | case HEX: | |||
437 | CHECK(str->Length() % 2 == 0 && "invalid hex string length")do { if (__builtin_expect(!!(!(str->Length() % 2 == 0 && "invalid hex string length")), 0)) { do { static const node:: AssertionInfo args = { "../src/string_bytes.cc" ":" "437", "str->Length() % 2 == 0 && \"invalid hex string length\"" , __PRETTY_FUNCTION__ }; node::Assert(args); } while (0); } } while (0); | |||
438 | data_size = str->Length() / 2; | |||
439 | break; | |||
440 | ||||
441 | default: | |||
442 | CHECK(0 && "unknown encoding")do { if (__builtin_expect(!!(!(0 && "unknown encoding" )), 0)) { do { static const node::AssertionInfo args = { "../src/string_bytes.cc" ":" "442", "0 && \"unknown encoding\"", __PRETTY_FUNCTION__ }; node::Assert(args); } while (0); } } while (0); | |||
443 | break; | |||
444 | } | |||
445 | ||||
446 | return Just(data_size); | |||
447 | } | |||
448 | ||||
449 | Maybe<size_t> StringBytes::Size(Isolate* isolate, | |||
450 | Local<Value> val, | |||
451 | enum encoding encoding) { | |||
452 | HandleScope scope(isolate); | |||
453 | ||||
454 | if (Buffer::HasInstance(val) && (encoding == BUFFER || encoding == LATIN1)) | |||
455 | return Just(Buffer::Length(val)); | |||
456 | ||||
457 | Local<String> str; | |||
458 | if (!val->ToString(isolate->GetCurrentContext()).ToLocal(&str)) | |||
459 | return Nothing<size_t>(); | |||
460 | ||||
461 | switch (encoding) { | |||
462 | case ASCII: | |||
463 | case LATIN1: | |||
464 | return Just<size_t>(str->Length()); | |||
465 | ||||
466 | case BUFFER: | |||
467 | case UTF8: | |||
468 | return Just<size_t>(str->Utf8Length(isolate)); | |||
469 | ||||
470 | case UCS2: | |||
471 | return Just(str->Length() * sizeof(uint16_t)); | |||
472 | ||||
473 | case BASE64URL: | |||
474 | // Fall through | |||
475 | case BASE64: { | |||
476 | String::Value value(isolate, str); | |||
477 | return Just(base64_decoded_size(*value, value.length())); | |||
478 | } | |||
479 | ||||
480 | case HEX: | |||
481 | return Just<size_t>(str->Length() / 2); | |||
482 | } | |||
483 | ||||
484 | UNREACHABLE()do { static const node::AssertionInfo args = { "../src/string_bytes.cc" ":" "484", "\"Unreachable code reached\"", __PRETTY_FUNCTION__ }; node::Assert(args); } while (0); | |||
485 | } | |||
486 | ||||
487 | ||||
488 | ||||
489 | ||||
490 | static bool contains_non_ascii_slow(const char* buf, size_t len) { | |||
491 | for (size_t i = 0; i < len; ++i) { | |||
492 | if (buf[i] & 0x80) | |||
493 | return true; | |||
494 | } | |||
495 | return false; | |||
496 | } | |||
497 | ||||
498 | ||||
499 | static bool contains_non_ascii(const char* src, size_t len) { | |||
500 | if (len < 16) { | |||
501 | return contains_non_ascii_slow(src, len); | |||
502 | } | |||
503 | ||||
504 | const unsigned bytes_per_word = sizeof(uintptr_t); | |||
505 | const unsigned align_mask = bytes_per_word - 1; | |||
506 | const unsigned unaligned = reinterpret_cast<uintptr_t>(src) & align_mask; | |||
507 | ||||
508 | if (unaligned > 0) { | |||
509 | const unsigned n = bytes_per_word - unaligned; | |||
510 | if (contains_non_ascii_slow(src, n)) | |||
511 | return true; | |||
512 | src += n; | |||
513 | len -= n; | |||
514 | } | |||
515 | ||||
516 | ||||
517 | #if defined(_WIN64) || defined(_LP641) | |||
518 | const uintptr_t mask = 0x8080808080808080ll; | |||
519 | #else | |||
520 | const uintptr_t mask = 0x80808080l; | |||
521 | #endif | |||
522 | ||||
523 | const uintptr_t* srcw = reinterpret_cast<const uintptr_t*>(src); | |||
524 | ||||
525 | for (size_t i = 0, n = len / bytes_per_word; i < n; ++i) { | |||
526 | if (srcw[i] & mask) | |||
527 | return true; | |||
528 | } | |||
529 | ||||
530 | const unsigned remainder = len & align_mask; | |||
531 | if (remainder > 0) { | |||
532 | const size_t offset = len - remainder; | |||
533 | if (contains_non_ascii_slow(src + offset, remainder)) | |||
534 | return true; | |||
535 | } | |||
536 | ||||
537 | return false; | |||
538 | } | |||
539 | ||||
540 | ||||
541 | static void force_ascii_slow(const char* src, char* dst, size_t len) { | |||
542 | for (size_t i = 0; i < len; ++i) { | |||
543 | dst[i] = src[i] & 0x7f; | |||
544 | } | |||
545 | } | |||
546 | ||||
547 | ||||
548 | static void force_ascii(const char* src, char* dst, size_t len) { | |||
549 | if (len < 16) { | |||
550 | force_ascii_slow(src, dst, len); | |||
551 | return; | |||
552 | } | |||
553 | ||||
554 | const unsigned bytes_per_word = sizeof(uintptr_t); | |||
555 | const unsigned align_mask = bytes_per_word - 1; | |||
556 | const unsigned src_unalign = reinterpret_cast<uintptr_t>(src) & align_mask; | |||
557 | const unsigned dst_unalign = reinterpret_cast<uintptr_t>(dst) & align_mask; | |||
558 | ||||
559 | if (src_unalign > 0) { | |||
560 | if (src_unalign == dst_unalign) { | |||
561 | const unsigned unalign = bytes_per_word - src_unalign; | |||
562 | force_ascii_slow(src, dst, unalign); | |||
563 | src += unalign; | |||
564 | dst += unalign; | |||
565 | len -= src_unalign; | |||
566 | } else { | |||
567 | force_ascii_slow(src, dst, len); | |||
568 | return; | |||
569 | } | |||
570 | } | |||
571 | ||||
572 | #if defined(_WIN64) || defined(_LP641) | |||
573 | const uintptr_t mask = ~0x8080808080808080ll; | |||
574 | #else | |||
575 | const uintptr_t mask = ~0x80808080l; | |||
576 | #endif | |||
577 | ||||
578 | const uintptr_t* srcw = reinterpret_cast<const uintptr_t*>(src); | |||
579 | uintptr_t* dstw = reinterpret_cast<uintptr_t*>(dst); | |||
580 | ||||
581 | for (size_t i = 0, n = len / bytes_per_word; i < n; ++i) { | |||
582 | dstw[i] = srcw[i] & mask; | |||
583 | } | |||
584 | ||||
585 | const unsigned remainder = len & align_mask; | |||
586 | if (remainder > 0) { | |||
587 | const size_t offset = len - remainder; | |||
588 | force_ascii_slow(src + offset, dst + offset, remainder); | |||
589 | } | |||
590 | } | |||
591 | ||||
592 | ||||
593 | size_t StringBytes::hex_encode( | |||
594 | const char* src, | |||
595 | size_t slen, | |||
596 | char* dst, | |||
597 | size_t dlen) { | |||
598 | // We know how much we'll write, just make sure that there's space. | |||
599 | CHECK(dlen >= slen * 2 &&do { if (__builtin_expect(!!(!(dlen >= slen * 2 && "not enough space provided for hex encode")), 0)) { do { static const node::AssertionInfo args = { "../src/string_bytes.cc" ":" "600", "dlen >= slen * 2 && \"not enough space provided for hex encode\"" , __PRETTY_FUNCTION__ }; node::Assert(args); } while (0); } } while (0) | |||
600 | "not enough space provided for hex encode")do { if (__builtin_expect(!!(!(dlen >= slen * 2 && "not enough space provided for hex encode")), 0)) { do { static const node::AssertionInfo args = { "../src/string_bytes.cc" ":" "600", "dlen >= slen * 2 && \"not enough space provided for hex encode\"" , __PRETTY_FUNCTION__ }; node::Assert(args); } while (0); } } while (0); | |||
601 | ||||
602 | dlen = slen * 2; | |||
603 | for (uint32_t i = 0, k = 0; k < dlen; i += 1, k += 2) { | |||
604 | static const char hex[] = "0123456789abcdef"; | |||
605 | uint8_t val = static_cast<uint8_t>(src[i]); | |||
606 | dst[k + 0] = hex[val >> 4]; | |||
607 | dst[k + 1] = hex[val & 15]; | |||
608 | } | |||
609 | ||||
610 | return dlen; | |||
611 | } | |||
612 | ||||
613 | std::string StringBytes::hex_encode(const char* src, size_t slen) { | |||
614 | size_t dlen = slen * 2; | |||
615 | std::string dst(dlen, '\0'); | |||
616 | hex_encode(src, slen, &dst[0], dlen); | |||
617 | return dst; | |||
618 | } | |||
619 | ||||
620 | #define CHECK_BUFLEN_IN_RANGE(len)do { if ((len) > Buffer::kMaxLength) { *error = node::ERR_BUFFER_TOO_LARGE (isolate); return MaybeLocal<Value>(); } } while (0) \ | |||
621 | do { \ | |||
622 | if ((len) > Buffer::kMaxLength) { \ | |||
623 | *error = node::ERR_BUFFER_TOO_LARGE(isolate); \ | |||
624 | return MaybeLocal<Value>(); \ | |||
625 | } \ | |||
626 | } while (0) | |||
627 | ||||
628 | ||||
629 | MaybeLocal<Value> StringBytes::Encode(Isolate* isolate, | |||
630 | const char* buf, | |||
631 | size_t buflen, | |||
632 | enum encoding encoding, | |||
633 | Local<Value>* error) { | |||
634 | CHECK_BUFLEN_IN_RANGE(buflen)do { if ((buflen) > Buffer::kMaxLength) { *error = node::ERR_BUFFER_TOO_LARGE (isolate); return MaybeLocal<Value>(); } } while (0); | |||
635 | ||||
636 | if (!buflen && encoding != BUFFER) { | |||
637 | return String::Empty(isolate); | |||
638 | } | |||
639 | ||||
640 | MaybeLocal<String> val; | |||
641 | ||||
642 | switch (encoding) { | |||
643 | case BUFFER: | |||
644 | { | |||
645 | auto maybe_buf = Buffer::Copy(isolate, buf, buflen); | |||
646 | Local<v8::Object> buf; | |||
647 | if (!maybe_buf.ToLocal(&buf)) { | |||
648 | *error = node::ERR_MEMORY_ALLOCATION_FAILED(isolate); | |||
649 | } | |||
650 | return buf; | |||
651 | } | |||
652 | ||||
653 | case ASCII: | |||
654 | if (contains_non_ascii(buf, buflen)) { | |||
655 | char* out = node::UncheckedMalloc(buflen); | |||
656 | if (out == nullptr) { | |||
657 | *error = node::ERR_MEMORY_ALLOCATION_FAILED(isolate); | |||
658 | return MaybeLocal<Value>(); | |||
659 | } | |||
660 | force_ascii(buf, out, buflen); | |||
661 | return ExternOneByteString::New(isolate, out, buflen, error); | |||
662 | } else { | |||
663 | return ExternOneByteString::NewFromCopy(isolate, buf, buflen, error); | |||
664 | } | |||
665 | ||||
666 | case UTF8: | |||
667 | { | |||
668 | val = String::NewFromUtf8(isolate, | |||
669 | buf, | |||
670 | v8::NewStringType::kNormal, | |||
671 | buflen); | |||
672 | Local<String> str; | |||
673 | if (!val.ToLocal(&str)) { | |||
674 | *error = node::ERR_STRING_TOO_LONG(isolate); | |||
675 | } | |||
676 | return str; | |||
677 | } | |||
678 | ||||
679 | case LATIN1: | |||
680 | return ExternOneByteString::NewFromCopy(isolate, buf, buflen, error); | |||
681 | ||||
682 | case BASE64: { | |||
683 | size_t dlen = base64_encoded_size(buflen); | |||
684 | char* dst = node::UncheckedMalloc(dlen); | |||
685 | if (dst == nullptr) { | |||
686 | *error = node::ERR_MEMORY_ALLOCATION_FAILED(isolate); | |||
687 | return MaybeLocal<Value>(); | |||
688 | } | |||
689 | ||||
690 | size_t written = base64_encode(buf, buflen, dst, dlen); | |||
691 | CHECK_EQ(written, dlen)do { if (__builtin_expect(!!(!((written) == (dlen))), 0)) { do { static const node::AssertionInfo args = { "../src/string_bytes.cc" ":" "691", "(written) == (dlen)", __PRETTY_FUNCTION__ }; node ::Assert(args); } while (0); } } while (0); | |||
692 | ||||
693 | return ExternOneByteString::New(isolate, dst, dlen, error); | |||
694 | } | |||
695 | ||||
696 | case BASE64URL: { | |||
697 | size_t dlen = base64_encoded_size(buflen, Base64Mode::URL); | |||
698 | char* dst = node::UncheckedMalloc(dlen); | |||
699 | if (dst == nullptr) { | |||
700 | *error = node::ERR_MEMORY_ALLOCATION_FAILED(isolate); | |||
701 | return MaybeLocal<Value>(); | |||
702 | } | |||
703 | ||||
704 | size_t written = base64_encode(buf, buflen, dst, dlen, Base64Mode::URL); | |||
705 | CHECK_EQ(written, dlen)do { if (__builtin_expect(!!(!((written) == (dlen))), 0)) { do { static const node::AssertionInfo args = { "../src/string_bytes.cc" ":" "705", "(written) == (dlen)", __PRETTY_FUNCTION__ }; node ::Assert(args); } while (0); } } while (0); | |||
706 | ||||
707 | return ExternOneByteString::New(isolate, dst, dlen, error); | |||
708 | } | |||
709 | ||||
710 | case HEX: { | |||
711 | size_t dlen = buflen * 2; | |||
712 | char* dst = node::UncheckedMalloc(dlen); | |||
713 | if (dst == nullptr) { | |||
714 | *error = node::ERR_MEMORY_ALLOCATION_FAILED(isolate); | |||
715 | return MaybeLocal<Value>(); | |||
716 | } | |||
717 | size_t written = hex_encode(buf, buflen, dst, dlen); | |||
718 | CHECK_EQ(written, dlen)do { if (__builtin_expect(!!(!((written) == (dlen))), 0)) { do { static const node::AssertionInfo args = { "../src/string_bytes.cc" ":" "718", "(written) == (dlen)", __PRETTY_FUNCTION__ }; node ::Assert(args); } while (0); } } while (0); | |||
719 | ||||
720 | return ExternOneByteString::New(isolate, dst, dlen, error); | |||
721 | } | |||
722 | ||||
723 | case UCS2: { | |||
724 | if (IsBigEndian()) { | |||
725 | uint16_t* dst = node::UncheckedMalloc<uint16_t>(buflen / 2); | |||
726 | if (dst == nullptr) { | |||
727 | *error = node::ERR_MEMORY_ALLOCATION_FAILED(isolate); | |||
728 | return MaybeLocal<Value>(); | |||
729 | } | |||
730 | for (size_t i = 0, k = 0; k < buflen / 2; i += 2, k += 1) { | |||
731 | // The input is in *little endian*, because that's what Node.js | |||
732 | // expects, so the high byte comes after the low byte. | |||
733 | const uint8_t hi = static_cast<uint8_t>(buf[i + 1]); | |||
734 | const uint8_t lo = static_cast<uint8_t>(buf[i + 0]); | |||
735 | dst[k] = static_cast<uint16_t>(hi) << 8 | lo; | |||
736 | } | |||
737 | return ExternTwoByteString::New(isolate, dst, buflen / 2, error); | |||
738 | } | |||
739 | if (reinterpret_cast<uintptr_t>(buf) % 2 != 0) { | |||
740 | // Unaligned data still means we can't directly pass it to V8. | |||
741 | char* dst = node::UncheckedMalloc(buflen); | |||
742 | if (dst == nullptr) { | |||
743 | *error = node::ERR_MEMORY_ALLOCATION_FAILED(isolate); | |||
744 | return MaybeLocal<Value>(); | |||
745 | } | |||
746 | memcpy(dst, buf, buflen); | |||
747 | return ExternTwoByteString::New( | |||
748 | isolate, reinterpret_cast<uint16_t*>(dst), buflen / 2, error); | |||
749 | } | |||
750 | return ExternTwoByteString::NewFromCopy( | |||
751 | isolate, reinterpret_cast<const uint16_t*>(buf), buflen / 2, error); | |||
752 | } | |||
753 | ||||
754 | default: | |||
755 | CHECK(0 && "unknown encoding")do { if (__builtin_expect(!!(!(0 && "unknown encoding" )), 0)) { do { static const node::AssertionInfo args = { "../src/string_bytes.cc" ":" "755", "0 && \"unknown encoding\"", __PRETTY_FUNCTION__ }; node::Assert(args); } while (0); } } while (0); | |||
756 | break; | |||
757 | } | |||
758 | ||||
759 | UNREACHABLE()do { static const node::AssertionInfo args = { "../src/string_bytes.cc" ":" "759", "\"Unreachable code reached\"", __PRETTY_FUNCTION__ }; node::Assert(args); } while (0); | |||
760 | } | |||
761 | ||||
762 | ||||
763 | MaybeLocal<Value> StringBytes::Encode(Isolate* isolate, | |||
764 | const uint16_t* buf, | |||
765 | size_t buflen, | |||
766 | Local<Value>* error) { | |||
767 | CHECK_BUFLEN_IN_RANGE(buflen)do { if ((buflen) > Buffer::kMaxLength) { *error = node::ERR_BUFFER_TOO_LARGE (isolate); return MaybeLocal<Value>(); } } while (0); | |||
| ||||
768 | ||||
769 | // Node's "ucs2" encoding expects LE character data inside a | |||
770 | // Buffer, so we need to reorder on BE platforms. See | |||
771 | // https://nodejs.org/api/buffer.html regarding Node's "ucs2" | |||
772 | // encoding specification | |||
773 | if (IsBigEndian()) { | |||
774 | uint16_t* dst = node::UncheckedMalloc<uint16_t>(buflen); | |||
775 | if (dst == nullptr) { | |||
776 | *error = node::ERR_MEMORY_ALLOCATION_FAILED(isolate); | |||
777 | return MaybeLocal<Value>(); | |||
778 | } | |||
779 | size_t nbytes = buflen * sizeof(uint16_t); | |||
780 | memcpy(dst, buf, nbytes); | |||
781 | SwapBytes16(reinterpret_cast<char*>(dst), nbytes); | |||
782 | return ExternTwoByteString::New(isolate, dst, buflen, error); | |||
| ||||
783 | } else { | |||
784 | return ExternTwoByteString::NewFromCopy(isolate, buf, buflen, error); | |||
785 | } | |||
786 | } | |||
787 | ||||
788 | MaybeLocal<Value> StringBytes::Encode(Isolate* isolate, | |||
789 | const char* buf, | |||
790 | enum encoding encoding, | |||
791 | Local<Value>* error) { | |||
792 | const size_t len = strlen(buf); | |||
793 | return Encode(isolate, buf, len, encoding, error); | |||
794 | } | |||
795 | ||||
796 | } // namespace node |
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 | #ifndef SRC_UTIL_INL_H_ | ||||
23 | #define SRC_UTIL_INL_H_ | ||||
24 | |||||
25 | #if defined(NODE_WANT_INTERNALS1) && NODE_WANT_INTERNALS1 | ||||
26 | |||||
27 | #include <cmath> | ||||
28 | #include <cstring> | ||||
29 | #include <locale> | ||||
30 | #include "util.h" | ||||
31 | |||||
32 | // These are defined by <sys/byteorder.h> or <netinet/in.h> on some systems. | ||||
33 | // To avoid warnings, undefine them before redefining them. | ||||
34 | #ifdef BSWAP_2 | ||||
35 | # undef BSWAP_2 | ||||
36 | #endif | ||||
37 | #ifdef BSWAP_4 | ||||
38 | # undef BSWAP_4 | ||||
39 | #endif | ||||
40 | #ifdef BSWAP_8 | ||||
41 | # undef BSWAP_8 | ||||
42 | #endif | ||||
43 | |||||
44 | #if defined(_MSC_VER) | ||||
45 | #include <intrin.h> | ||||
46 | #define BSWAP_2(x)((x) << 8) | ((x) >> 8) _byteswap_ushort(x) | ||||
47 | #define BSWAP_4(x)(((x) & 0xFF) << 24) | (((x) & 0xFF00) << 8) | (((x) >> 8) & 0xFF00) | (((x) >> 24) & 0xFF) _byteswap_ulong(x) | ||||
48 | #define BSWAP_8(x)(((x) & 0xFF00000000000000ull) >> 56) | (((x) & 0x00FF000000000000ull) >> 40) | (((x) & 0x0000FF0000000000ull ) >> 24) | (((x) & 0x000000FF00000000ull) >> 8 ) | (((x) & 0x00000000FF000000ull) << 8) | (((x) & 0x0000000000FF0000ull) << 24) | (((x) & 0x000000000000FF00ull ) << 40) | (((x) & 0x00000000000000FFull) << 56 ) _byteswap_uint64(x) | ||||
49 | #else | ||||
50 | #define BSWAP_2(x)((x) << 8) | ((x) >> 8) ((x) << 8) | ((x) >> 8) | ||||
51 | #define BSWAP_4(x)(((x) & 0xFF) << 24) | (((x) & 0xFF00) << 8) | (((x) >> 8) & 0xFF00) | (((x) >> 24) & 0xFF) \ | ||||
52 | (((x) & 0xFF) << 24) | \ | ||||
53 | (((x) & 0xFF00) << 8) | \ | ||||
54 | (((x) >> 8) & 0xFF00) | \ | ||||
55 | (((x) >> 24) & 0xFF) | ||||
56 | #define BSWAP_8(x)(((x) & 0xFF00000000000000ull) >> 56) | (((x) & 0x00FF000000000000ull) >> 40) | (((x) & 0x0000FF0000000000ull ) >> 24) | (((x) & 0x000000FF00000000ull) >> 8 ) | (((x) & 0x00000000FF000000ull) << 8) | (((x) & 0x0000000000FF0000ull) << 24) | (((x) & 0x000000000000FF00ull ) << 40) | (((x) & 0x00000000000000FFull) << 56 ) \ | ||||
57 | (((x) & 0xFF00000000000000ull) >> 56) | \ | ||||
58 | (((x) & 0x00FF000000000000ull) >> 40) | \ | ||||
59 | (((x) & 0x0000FF0000000000ull) >> 24) | \ | ||||
60 | (((x) & 0x000000FF00000000ull) >> 8) | \ | ||||
61 | (((x) & 0x00000000FF000000ull) << 8) | \ | ||||
62 | (((x) & 0x0000000000FF0000ull) << 24) | \ | ||||
63 | (((x) & 0x000000000000FF00ull) << 40) | \ | ||||
64 | (((x) & 0x00000000000000FFull) << 56) | ||||
65 | #endif | ||||
66 | |||||
67 | #define CHAR_TEST(bits, name, expr)template <typename T> bool name(const T ch) { static_assert (sizeof(ch) >= (bits) / 8, "Character must be wider than " "bits" " bits"); return (expr); } \ | ||||
68 | template <typename T> \ | ||||
69 | bool name(const T ch) { \ | ||||
70 | static_assert(sizeof(ch) >= (bits) / 8, \ | ||||
71 | "Character must be wider than " #bits " bits"); \ | ||||
72 | return (expr); \ | ||||
73 | } | ||||
74 | |||||
75 | namespace node { | ||||
76 | |||||
77 | template <typename T> | ||||
78 | ListNode<T>::ListNode() : prev_(this), next_(this) {} | ||||
79 | |||||
80 | template <typename T> | ||||
81 | ListNode<T>::~ListNode() { | ||||
82 | Remove(); | ||||
83 | } | ||||
84 | |||||
85 | template <typename T> | ||||
86 | void ListNode<T>::Remove() { | ||||
87 | prev_->next_ = next_; | ||||
88 | next_->prev_ = prev_; | ||||
89 | prev_ = this; | ||||
90 | next_ = this; | ||||
91 | } | ||||
92 | |||||
93 | template <typename T> | ||||
94 | bool ListNode<T>::IsEmpty() const { | ||||
95 | return prev_ == this; | ||||
96 | } | ||||
97 | |||||
98 | template <typename T, ListNode<T> (T::*M)> | ||||
99 | ListHead<T, M>::Iterator::Iterator(ListNode<T>* node) : node_(node) {} | ||||
100 | |||||
101 | template <typename T, ListNode<T> (T::*M)> | ||||
102 | T* ListHead<T, M>::Iterator::operator*() const { | ||||
103 | return ContainerOf(M, node_); | ||||
104 | } | ||||
105 | |||||
106 | template <typename T, ListNode<T> (T::*M)> | ||||
107 | const typename ListHead<T, M>::Iterator& | ||||
108 | ListHead<T, M>::Iterator::operator++() { | ||||
109 | node_ = node_->next_; | ||||
110 | return *this; | ||||
111 | } | ||||
112 | |||||
113 | template <typename T, ListNode<T> (T::*M)> | ||||
114 | bool ListHead<T, M>::Iterator::operator!=(const Iterator& that) const { | ||||
115 | return node_ != that.node_; | ||||
116 | } | ||||
117 | |||||
118 | template <typename T, ListNode<T> (T::*M)> | ||||
119 | ListHead<T, M>::~ListHead() { | ||||
120 | while (IsEmpty() == false) | ||||
121 | head_.next_->Remove(); | ||||
122 | } | ||||
123 | |||||
124 | template <typename T, ListNode<T> (T::*M)> | ||||
125 | void ListHead<T, M>::PushBack(T* element) { | ||||
126 | ListNode<T>* that = &(element->*M); | ||||
127 | head_.prev_->next_ = that; | ||||
128 | that->prev_ = head_.prev_; | ||||
129 | that->next_ = &head_; | ||||
130 | head_.prev_ = that; | ||||
131 | } | ||||
132 | |||||
133 | template <typename T, ListNode<T> (T::*M)> | ||||
134 | void ListHead<T, M>::PushFront(T* element) { | ||||
135 | ListNode<T>* that = &(element->*M); | ||||
136 | head_.next_->prev_ = that; | ||||
137 | that->prev_ = &head_; | ||||
138 | that->next_ = head_.next_; | ||||
139 | head_.next_ = that; | ||||
140 | } | ||||
141 | |||||
142 | template <typename T, ListNode<T> (T::*M)> | ||||
143 | bool ListHead<T, M>::IsEmpty() const { | ||||
144 | return head_.IsEmpty(); | ||||
145 | } | ||||
146 | |||||
147 | template <typename T, ListNode<T> (T::*M)> | ||||
148 | T* ListHead<T, M>::PopFront() { | ||||
149 | if (IsEmpty()) | ||||
150 | return nullptr; | ||||
151 | ListNode<T>* node = head_.next_; | ||||
152 | node->Remove(); | ||||
153 | return ContainerOf(M, node); | ||||
154 | } | ||||
155 | |||||
156 | template <typename T, ListNode<T> (T::*M)> | ||||
157 | typename ListHead<T, M>::Iterator ListHead<T, M>::begin() const { | ||||
158 | return Iterator(head_.next_); | ||||
159 | } | ||||
160 | |||||
161 | template <typename T, ListNode<T> (T::*M)> | ||||
162 | typename ListHead<T, M>::Iterator ListHead<T, M>::end() const { | ||||
163 | return Iterator(const_cast<ListNode<T>*>(&head_)); | ||||
164 | } | ||||
165 | |||||
166 | template <typename Inner, typename Outer> | ||||
167 | constexpr uintptr_t OffsetOf(Inner Outer::*field) { | ||||
168 | return reinterpret_cast<uintptr_t>(&(static_cast<Outer*>(nullptr)->*field)); | ||||
169 | } | ||||
170 | |||||
171 | template <typename Inner, typename Outer> | ||||
172 | ContainerOfHelper<Inner, Outer>::ContainerOfHelper(Inner Outer::*field, | ||||
173 | Inner* pointer) | ||||
174 | : pointer_( | ||||
175 | reinterpret_cast<Outer*>( | ||||
176 | reinterpret_cast<uintptr_t>(pointer) - OffsetOf(field))) {} | ||||
177 | |||||
178 | template <typename Inner, typename Outer> | ||||
179 | template <typename TypeName> | ||||
180 | ContainerOfHelper<Inner, Outer>::operator TypeName*() const { | ||||
181 | return static_cast<TypeName*>(pointer_); | ||||
182 | } | ||||
183 | |||||
184 | template <typename Inner, typename Outer> | ||||
185 | constexpr ContainerOfHelper<Inner, Outer> ContainerOf(Inner Outer::*field, | ||||
186 | Inner* pointer) { | ||||
187 | return ContainerOfHelper<Inner, Outer>(field, pointer); | ||||
188 | } | ||||
189 | |||||
190 | inline v8::Local<v8::String> OneByteString(v8::Isolate* isolate, | ||||
191 | const char* data, | ||||
192 | int length) { | ||||
193 | return v8::String::NewFromOneByte(isolate, | ||||
194 | reinterpret_cast<const uint8_t*>(data), | ||||
195 | v8::NewStringType::kNormal, | ||||
196 | length).ToLocalChecked(); | ||||
197 | } | ||||
198 | |||||
199 | inline v8::Local<v8::String> OneByteString(v8::Isolate* isolate, | ||||
200 | const signed char* data, | ||||
201 | int length) { | ||||
202 | return v8::String::NewFromOneByte(isolate, | ||||
203 | reinterpret_cast<const uint8_t*>(data), | ||||
204 | v8::NewStringType::kNormal, | ||||
205 | length).ToLocalChecked(); | ||||
206 | } | ||||
207 | |||||
208 | inline v8::Local<v8::String> OneByteString(v8::Isolate* isolate, | ||||
209 | const unsigned char* data, | ||||
210 | int length) { | ||||
211 | return v8::String::NewFromOneByte( | ||||
212 | isolate, data, v8::NewStringType::kNormal, length) | ||||
213 | .ToLocalChecked(); | ||||
214 | } | ||||
215 | |||||
216 | void SwapBytes16(char* data, size_t nbytes) { | ||||
217 | CHECK_EQ(nbytes % 2, 0)do { if (__builtin_expect(!!(!((nbytes % 2) == (0))), 0)) { do { static const node::AssertionInfo args = { "../src/util-inl.h" ":" "217", "(nbytes % 2) == (0)", __PRETTY_FUNCTION__ }; node ::Assert(args); } while (0); } } while (0); | ||||
218 | |||||
219 | #if defined(_MSC_VER) | ||||
220 | if (AlignUp(data, sizeof(uint16_t)) == data) { | ||||
221 | // MSVC has no strict aliasing, and is able to highly optimize this case. | ||||
222 | uint16_t* data16 = reinterpret_cast<uint16_t*>(data); | ||||
223 | size_t len16 = nbytes / sizeof(*data16); | ||||
224 | for (size_t i = 0; i < len16; i++) { | ||||
225 | data16[i] = BSWAP_2(data16[i])((data16[i]) << 8) | ((data16[i]) >> 8); | ||||
226 | } | ||||
227 | return; | ||||
228 | } | ||||
229 | #endif | ||||
230 | |||||
231 | uint16_t temp; | ||||
232 | for (size_t i = 0; i < nbytes; i += sizeof(temp)) { | ||||
233 | memcpy(&temp, &data[i], sizeof(temp)); | ||||
234 | temp = BSWAP_2(temp)((temp) << 8) | ((temp) >> 8); | ||||
235 | memcpy(&data[i], &temp, sizeof(temp)); | ||||
236 | } | ||||
237 | } | ||||
238 | |||||
239 | void SwapBytes32(char* data, size_t nbytes) { | ||||
240 | CHECK_EQ(nbytes % 4, 0)do { if (__builtin_expect(!!(!((nbytes % 4) == (0))), 0)) { do { static const node::AssertionInfo args = { "../src/util-inl.h" ":" "240", "(nbytes % 4) == (0)", __PRETTY_FUNCTION__ }; node ::Assert(args); } while (0); } } while (0); | ||||
241 | |||||
242 | #if defined(_MSC_VER) | ||||
243 | // MSVC has no strict aliasing, and is able to highly optimize this case. | ||||
244 | if (AlignUp(data, sizeof(uint32_t)) == data) { | ||||
245 | uint32_t* data32 = reinterpret_cast<uint32_t*>(data); | ||||
246 | size_t len32 = nbytes / sizeof(*data32); | ||||
247 | for (size_t i = 0; i < len32; i++) { | ||||
248 | data32[i] = BSWAP_4(data32[i])(((data32[i]) & 0xFF) << 24) | (((data32[i]) & 0xFF00 ) << 8) | (((data32[i]) >> 8) & 0xFF00) | ((( data32[i]) >> 24) & 0xFF); | ||||
249 | } | ||||
250 | return; | ||||
251 | } | ||||
252 | #endif | ||||
253 | |||||
254 | uint32_t temp; | ||||
255 | for (size_t i = 0; i < nbytes; i += sizeof(temp)) { | ||||
256 | memcpy(&temp, &data[i], sizeof(temp)); | ||||
257 | temp = BSWAP_4(temp)(((temp) & 0xFF) << 24) | (((temp) & 0xFF00) << 8) | (((temp) >> 8) & 0xFF00) | (((temp) >> 24 ) & 0xFF); | ||||
258 | memcpy(&data[i], &temp, sizeof(temp)); | ||||
259 | } | ||||
260 | } | ||||
261 | |||||
262 | void SwapBytes64(char* data, size_t nbytes) { | ||||
263 | CHECK_EQ(nbytes % 8, 0)do { if (__builtin_expect(!!(!((nbytes % 8) == (0))), 0)) { do { static const node::AssertionInfo args = { "../src/util-inl.h" ":" "263", "(nbytes % 8) == (0)", __PRETTY_FUNCTION__ }; node ::Assert(args); } while (0); } } while (0); | ||||
264 | |||||
265 | #if defined(_MSC_VER) | ||||
266 | if (AlignUp(data, sizeof(uint64_t)) == data) { | ||||
267 | // MSVC has no strict aliasing, and is able to highly optimize this case. | ||||
268 | uint64_t* data64 = reinterpret_cast<uint64_t*>(data); | ||||
269 | size_t len64 = nbytes / sizeof(*data64); | ||||
270 | for (size_t i = 0; i < len64; i++) { | ||||
271 | data64[i] = BSWAP_8(data64[i])(((data64[i]) & 0xFF00000000000000ull) >> 56) | ((( data64[i]) & 0x00FF000000000000ull) >> 40) | (((data64 [i]) & 0x0000FF0000000000ull) >> 24) | (((data64[i] ) & 0x000000FF00000000ull) >> 8) | (((data64[i]) & 0x00000000FF000000ull) << 8) | (((data64[i]) & 0x0000000000FF0000ull ) << 24) | (((data64[i]) & 0x000000000000FF00ull) << 40) | (((data64[i]) & 0x00000000000000FFull) << 56 ); | ||||
272 | } | ||||
273 | return; | ||||
274 | } | ||||
275 | #endif | ||||
276 | |||||
277 | uint64_t temp; | ||||
278 | for (size_t i = 0; i < nbytes; i += sizeof(temp)) { | ||||
279 | memcpy(&temp, &data[i], sizeof(temp)); | ||||
280 | temp = BSWAP_8(temp)(((temp) & 0xFF00000000000000ull) >> 56) | (((temp) & 0x00FF000000000000ull) >> 40) | (((temp) & 0x0000FF0000000000ull ) >> 24) | (((temp) & 0x000000FF00000000ull) >> 8) | (((temp) & 0x00000000FF000000ull) << 8) | ((( temp) & 0x0000000000FF0000ull) << 24) | (((temp) & 0x000000000000FF00ull) << 40) | (((temp) & 0x00000000000000FFull ) << 56); | ||||
281 | memcpy(&data[i], &temp, sizeof(temp)); | ||||
282 | } | ||||
283 | } | ||||
284 | |||||
285 | char ToLower(char c) { | ||||
286 | return std::tolower(c, std::locale::classic()); | ||||
287 | } | ||||
288 | |||||
289 | std::string ToLower(const std::string& in) { | ||||
290 | std::string out(in.size(), 0); | ||||
291 | for (size_t i = 0; i < in.size(); ++i) | ||||
292 | out[i] = ToLower(in[i]); | ||||
293 | return out; | ||||
294 | } | ||||
295 | |||||
296 | char ToUpper(char c) { | ||||
297 | return std::toupper(c, std::locale::classic()); | ||||
298 | } | ||||
299 | |||||
300 | std::string ToUpper(const std::string& in) { | ||||
301 | std::string out(in.size(), 0); | ||||
302 | for (size_t i = 0; i < in.size(); ++i) | ||||
303 | out[i] = ToUpper(in[i]); | ||||
304 | return out; | ||||
305 | } | ||||
306 | |||||
307 | bool StringEqualNoCase(const char* a, const char* b) { | ||||
308 | while (ToLower(*a) == ToLower(*b++)) { | ||||
309 | if (*a++ == '\0') | ||||
310 | return true; | ||||
311 | } | ||||
312 | return false; | ||||
313 | } | ||||
314 | |||||
315 | bool StringEqualNoCaseN(const char* a, const char* b, size_t length) { | ||||
316 | for (size_t i = 0; i < length; i++) { | ||||
317 | if (ToLower(a[i]) != ToLower(b[i])) | ||||
318 | return false; | ||||
319 | if (a[i] == '\0') | ||||
320 | return true; | ||||
321 | } | ||||
322 | return true; | ||||
323 | } | ||||
324 | |||||
325 | template <typename T> | ||||
326 | inline T MultiplyWithOverflowCheck(T a, T b) { | ||||
327 | auto ret = a * b; | ||||
328 | if (a != 0) | ||||
329 | CHECK_EQ(b, ret / a)do { if (__builtin_expect(!!(!((b) == (ret / a))), 0)) { do { static const node::AssertionInfo args = { "../src/util-inl.h" ":" "329", "(b) == (ret / a)", __PRETTY_FUNCTION__ }; node:: Assert(args); } while (0); } } while (0); | ||||
330 | |||||
331 | return ret; | ||||
332 | } | ||||
333 | |||||
334 | // These should be used in our code as opposed to the native | ||||
335 | // versions as they abstract out some platform and or | ||||
336 | // compiler version specific functionality. | ||||
337 | // malloc(0) and realloc(ptr, 0) have implementation-defined behavior in | ||||
338 | // that the standard allows them to either return a unique pointer or a | ||||
339 | // nullptr for zero-sized allocation requests. Normalize by always using | ||||
340 | // a nullptr. | ||||
341 | template <typename T> | ||||
342 | T* UncheckedRealloc(T* pointer, size_t n) { | ||||
343 | size_t full_size = MultiplyWithOverflowCheck(sizeof(T), n); | ||||
344 | |||||
345 | if (full_size
| ||||
346 | free(pointer); | ||||
347 | return nullptr; | ||||
348 | } | ||||
349 | |||||
350 | void* allocated = realloc(pointer, full_size); | ||||
351 | |||||
352 | if (UNLIKELY(allocated == nullptr)__builtin_expect(!!(allocated == nullptr), 0)) { | ||||
353 | // Tell V8 that memory is low and retry. | ||||
354 | LowMemoryNotification(); | ||||
355 | allocated = realloc(pointer, full_size); | ||||
356 | } | ||||
357 | |||||
358 | return static_cast<T*>(allocated); | ||||
359 | } | ||||
360 | |||||
361 | // As per spec realloc behaves like malloc if passed nullptr. | ||||
362 | template <typename T> | ||||
363 | inline T* UncheckedMalloc(size_t n) { | ||||
364 | if (n == 0) n = 1; | ||||
365 | return UncheckedRealloc<T>(nullptr, n); | ||||
366 | } | ||||
367 | |||||
368 | template <typename T> | ||||
369 | inline T* UncheckedCalloc(size_t n) { | ||||
370 | if (n == 0) n = 1; | ||||
371 | MultiplyWithOverflowCheck(sizeof(T), n); | ||||
372 | return static_cast<T*>(calloc(n, sizeof(T))); | ||||
373 | } | ||||
374 | |||||
375 | template <typename T> | ||||
376 | inline T* Realloc(T* pointer, size_t n) { | ||||
377 | T* ret = UncheckedRealloc(pointer, n); | ||||
378 | CHECK_IMPLIES(n > 0, ret != nullptr)do { if (__builtin_expect(!!(!(!(n > 0) || (ret != nullptr ))), 0)) { do { static const node::AssertionInfo args = { "../src/util-inl.h" ":" "378", "!(n > 0) || (ret != nullptr)", __PRETTY_FUNCTION__ }; node::Assert(args); } while (0); } } while (0); | ||||
379 | return ret; | ||||
380 | } | ||||
381 | |||||
382 | template <typename T> | ||||
383 | inline T* Malloc(size_t n) { | ||||
384 | T* ret = UncheckedMalloc<T>(n); | ||||
385 | CHECK_IMPLIES(n > 0, ret != nullptr)do { if (__builtin_expect(!!(!(!(n > 0) || (ret != nullptr ))), 0)) { do { static const node::AssertionInfo args = { "../src/util-inl.h" ":" "385", "!(n > 0) || (ret != nullptr)", __PRETTY_FUNCTION__ }; node::Assert(args); } while (0); } } while (0); | ||||
386 | return ret; | ||||
387 | } | ||||
388 | |||||
389 | template <typename T> | ||||
390 | inline T* Calloc(size_t n) { | ||||
391 | T* ret = UncheckedCalloc<T>(n); | ||||
392 | CHECK_IMPLIES(n > 0, ret != nullptr)do { if (__builtin_expect(!!(!(!(n > 0) || (ret != nullptr ))), 0)) { do { static const node::AssertionInfo args = { "../src/util-inl.h" ":" "392", "!(n > 0) || (ret != nullptr)", __PRETTY_FUNCTION__ }; node::Assert(args); } while (0); } } while (0); | ||||
393 | return ret; | ||||
394 | } | ||||
395 | |||||
396 | // Shortcuts for char*. | ||||
397 | inline char* Malloc(size_t n) { return Malloc<char>(n); } | ||||
398 | inline char* Calloc(size_t n) { return Calloc<char>(n); } | ||||
399 | inline char* UncheckedMalloc(size_t n) { return UncheckedMalloc<char>(n); } | ||||
400 | inline char* UncheckedCalloc(size_t n) { return UncheckedCalloc<char>(n); } | ||||
401 | |||||
402 | // This is a helper in the .cc file so including util-inl.h doesn't include more | ||||
403 | // headers than we really need to. | ||||
404 | void ThrowErrStringTooLong(v8::Isolate* isolate); | ||||
405 | |||||
406 | v8::MaybeLocal<v8::Value> ToV8Value(v8::Local<v8::Context> context, | ||||
407 | std::string_view str, | ||||
408 | v8::Isolate* isolate) { | ||||
409 | if (isolate == nullptr) isolate = context->GetIsolate(); | ||||
410 | if (UNLIKELY(str.size() >= static_cast<size_t>(v8::String::kMaxLength))__builtin_expect(!!(str.size() >= static_cast<size_t> (v8::String::kMaxLength)), 0)) { | ||||
411 | // V8 only has a TODO comment about adding an exception when the maximum | ||||
412 | // string size is exceeded. | ||||
413 | ThrowErrStringTooLong(isolate); | ||||
414 | return v8::MaybeLocal<v8::Value>(); | ||||
415 | } | ||||
416 | |||||
417 | return v8::String::NewFromUtf8( | ||||
418 | isolate, str.data(), v8::NewStringType::kNormal, str.size()) | ||||
419 | .FromMaybe(v8::Local<v8::String>()); | ||||
420 | } | ||||
421 | |||||
422 | template <typename T> | ||||
423 | v8::MaybeLocal<v8::Value> ToV8Value(v8::Local<v8::Context> context, | ||||
424 | const std::vector<T>& vec, | ||||
425 | v8::Isolate* isolate) { | ||||
426 | if (isolate == nullptr) isolate = context->GetIsolate(); | ||||
427 | v8::EscapableHandleScope handle_scope(isolate); | ||||
428 | |||||
429 | MaybeStackBuffer<v8::Local<v8::Value>, 128> arr(vec.size()); | ||||
430 | arr.SetLength(vec.size()); | ||||
431 | for (size_t i = 0; i < vec.size(); ++i) { | ||||
432 | if (!ToV8Value(context, vec[i], isolate).ToLocal(&arr[i])) | ||||
433 | return v8::MaybeLocal<v8::Value>(); | ||||
434 | } | ||||
435 | |||||
436 | return handle_scope.Escape(v8::Array::New(isolate, arr.out(), arr.length())); | ||||
437 | } | ||||
438 | |||||
439 | template <typename T> | ||||
440 | v8::MaybeLocal<v8::Value> ToV8Value(v8::Local<v8::Context> context, | ||||
441 | const std::set<T>& set, | ||||
442 | v8::Isolate* isolate) { | ||||
443 | if (isolate == nullptr) isolate = context->GetIsolate(); | ||||
444 | v8::Local<v8::Set> set_js = v8::Set::New(isolate); | ||||
445 | v8::HandleScope handle_scope(isolate); | ||||
446 | |||||
447 | for (const T& entry : set) { | ||||
448 | v8::Local<v8::Value> value; | ||||
449 | if (!ToV8Value(context, entry, isolate).ToLocal(&value)) | ||||
450 | return {}; | ||||
451 | if (set_js->Add(context, value).IsEmpty()) | ||||
452 | return {}; | ||||
453 | } | ||||
454 | |||||
455 | return set_js; | ||||
456 | } | ||||
457 | |||||
458 | template <typename T, typename U> | ||||
459 | v8::MaybeLocal<v8::Value> ToV8Value(v8::Local<v8::Context> context, | ||||
460 | const std::unordered_map<T, U>& map, | ||||
461 | v8::Isolate* isolate) { | ||||
462 | if (isolate == nullptr) isolate = context->GetIsolate(); | ||||
463 | v8::EscapableHandleScope handle_scope(isolate); | ||||
464 | |||||
465 | v8::Local<v8::Map> ret = v8::Map::New(isolate); | ||||
466 | for (const auto& item : map) { | ||||
467 | v8::Local<v8::Value> first, second; | ||||
468 | if (!ToV8Value(context, item.first, isolate).ToLocal(&first) || | ||||
469 | !ToV8Value(context, item.second, isolate).ToLocal(&second) || | ||||
470 | ret->Set(context, first, second).IsEmpty()) { | ||||
471 | return v8::MaybeLocal<v8::Value>(); | ||||
472 | } | ||||
473 | } | ||||
474 | |||||
475 | return handle_scope.Escape(ret); | ||||
476 | } | ||||
477 | |||||
478 | template <typename T, typename > | ||||
479 | v8::MaybeLocal<v8::Value> ToV8Value(v8::Local<v8::Context> context, | ||||
480 | const T& number, | ||||
481 | v8::Isolate* isolate) { | ||||
482 | if (isolate == nullptr) isolate = context->GetIsolate(); | ||||
483 | |||||
484 | using Limits = std::numeric_limits<T>; | ||||
485 | // Choose Uint32, Int32, or Double depending on range checks. | ||||
486 | // These checks should all collapse at compile time. | ||||
487 | if (static_cast<uint32_t>(Limits::max()) <= | ||||
488 | std::numeric_limits<uint32_t>::max() && | ||||
489 | static_cast<uint32_t>(Limits::min()) >= | ||||
490 | std::numeric_limits<uint32_t>::min() && Limits::is_exact) { | ||||
491 | return v8::Integer::NewFromUnsigned(isolate, static_cast<uint32_t>(number)); | ||||
492 | } | ||||
493 | |||||
494 | if (static_cast<int32_t>(Limits::max()) <= | ||||
495 | std::numeric_limits<int32_t>::max() && | ||||
496 | static_cast<int32_t>(Limits::min()) >= | ||||
497 | std::numeric_limits<int32_t>::min() && Limits::is_exact) { | ||||
498 | return v8::Integer::New(isolate, static_cast<int32_t>(number)); | ||||
499 | } | ||||
500 | |||||
501 | return v8::Number::New(isolate, static_cast<double>(number)); | ||||
502 | } | ||||
503 | |||||
504 | SlicedArguments::SlicedArguments( | ||||
505 | const v8::FunctionCallbackInfo<v8::Value>& args, size_t start) { | ||||
506 | const size_t length = static_cast<size_t>(args.Length()); | ||||
507 | if (start >= length) return; | ||||
508 | const size_t size = length - start; | ||||
509 | |||||
510 | AllocateSufficientStorage(size); | ||||
511 | for (size_t i = 0; i < size; ++i) | ||||
512 | (*this)[i] = args[i + start]; | ||||
513 | } | ||||
514 | |||||
515 | template <typename T, size_t S> | ||||
516 | ArrayBufferViewContents<T, S>::ArrayBufferViewContents( | ||||
517 | v8::Local<v8::Value> value) { | ||||
518 | CHECK(value->IsArrayBufferView())do { if (__builtin_expect(!!(!(value->IsArrayBufferView()) ), 0)) { do { static const node::AssertionInfo args = { "../src/util-inl.h" ":" "518", "value->IsArrayBufferView()", __PRETTY_FUNCTION__ }; node::Assert(args); } while (0); } } while (0); | ||||
519 | Read(value.As<v8::ArrayBufferView>()); | ||||
520 | } | ||||
521 | |||||
522 | template <typename T, size_t S> | ||||
523 | ArrayBufferViewContents<T, S>::ArrayBufferViewContents( | ||||
524 | v8::Local<v8::Object> value) { | ||||
525 | CHECK(value->IsArrayBufferView())do { if (__builtin_expect(!!(!(value->IsArrayBufferView()) ), 0)) { do { static const node::AssertionInfo args = { "../src/util-inl.h" ":" "525", "value->IsArrayBufferView()", __PRETTY_FUNCTION__ }; node::Assert(args); } while (0); } } while (0); | ||||
526 | Read(value.As<v8::ArrayBufferView>()); | ||||
527 | } | ||||
528 | |||||
529 | template <typename T, size_t S> | ||||
530 | ArrayBufferViewContents<T, S>::ArrayBufferViewContents( | ||||
531 | v8::Local<v8::ArrayBufferView> abv) { | ||||
532 | Read(abv); | ||||
533 | } | ||||
534 | |||||
535 | template <typename T, size_t S> | ||||
536 | void ArrayBufferViewContents<T, S>::Read(v8::Local<v8::ArrayBufferView> abv) { | ||||
537 | static_assert(sizeof(T) == 1, "Only supports one-byte data at the moment"); | ||||
538 | length_ = abv->ByteLength(); | ||||
539 | if (length_ > sizeof(stack_storage_) || abv->HasBuffer()) { | ||||
540 | data_ = static_cast<T*>(abv->Buffer()->GetBackingStore()->Data()) + | ||||
541 | abv->ByteOffset(); | ||||
542 | } else { | ||||
543 | abv->CopyContents(stack_storage_, sizeof(stack_storage_)); | ||||
544 | data_ = stack_storage_; | ||||
545 | } | ||||
546 | } | ||||
547 | |||||
548 | // ECMA262 20.1.2.5 | ||||
549 | inline bool IsSafeJsInt(v8::Local<v8::Value> v) { | ||||
550 | if (!v->IsNumber()) return false; | ||||
551 | double v_d = v.As<v8::Number>()->Value(); | ||||
552 | if (std::isnan(v_d)) return false; | ||||
553 | if (std::isinf(v_d)) return false; | ||||
554 | if (std::trunc(v_d) != v_d) return false; // not int | ||||
555 | if (std::abs(v_d) <= static_cast<double>(kMaxSafeJsInteger)) return true; | ||||
556 | return false; | ||||
557 | } | ||||
558 | |||||
559 | constexpr size_t FastStringKey::HashImpl(const char* str) { | ||||
560 | // Low-quality hash (djb2), but just fine for current use cases. | ||||
561 | size_t h = 5381; | ||||
562 | while (*str != '\0') { | ||||
563 | h = h * 33 + *(str++); // NOLINT(readability/pointer_notation) | ||||
564 | } | ||||
565 | return h; | ||||
566 | } | ||||
567 | |||||
568 | constexpr size_t FastStringKey::Hash::operator()( | ||||
569 | const FastStringKey& key) const { | ||||
570 | return key.cached_hash_; | ||||
571 | } | ||||
572 | |||||
573 | constexpr bool FastStringKey::operator==(const FastStringKey& other) const { | ||||
574 | const char* p1 = name_; | ||||
575 | const char* p2 = other.name_; | ||||
576 | if (p1 == p2) return true; | ||||
577 | do { | ||||
578 | if (*(p1++) != *(p2++)) return false; | ||||
579 | } while (*p1 != '\0'); | ||||
580 | return *p2 == '\0'; | ||||
581 | } | ||||
582 | |||||
583 | constexpr FastStringKey::FastStringKey(const char* name) | ||||
584 | : name_(name), cached_hash_(HashImpl(name)) {} | ||||
585 | |||||
586 | constexpr const char* FastStringKey::c_str() const { | ||||
587 | return name_; | ||||
588 | } | ||||
589 | |||||
590 | } // namespace node | ||||
591 | |||||
592 | #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS | ||||
593 | |||||
594 | #endif // SRC_UTIL_INL_H_ |