| 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_ |