Where Online Learning is simpler!
The C and C++ Include Header Files
cat -n /usr/include/nodejs/src/util-inl.h
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_INTERNALS) && NODE_WANT_INTERNALS 26 27 #include <cmath> 28 #include <cstring> 29 #include <locale> 30 #include "node_revert.h" 31 #include "util.h" 32 33 #ifdef _WIN32 34 #include <regex> // NOLINT(build/c++11) 35 #endif // _WIN32 36 37 #define CHAR_TEST(bits, name, expr) \ 38 template <typename T> \ 39 bool name(const T ch) { \ 40 static_assert(sizeof(ch) >= (bits) / 8, \ 41 "Character must be wider than " #bits " bits"); \ 42 return (expr); \ 43 } 44 45 namespace node { 46 47 template <typename T> 48 ListNode<T>::ListNode() : prev_(this), next_(this) {} 49 50 template <typename T> 51 ListNode<T>::~ListNode() { 52 Remove(); 53 } 54 55 template <typename T> 56 void ListNode<T>::Remove() { 57 prev_->next_ = next_; 58 next_->prev_ = prev_; 59 prev_ = this; 60 next_ = this; 61 } 62 63 template <typename T> 64 bool ListNode<T>::IsEmpty() const { 65 return prev_ == this; 66 } 67 68 template <typename T, ListNode<T> (T::*M)> 69 ListHead<T, M>::Iterator::Iterator(ListNode<T>* node) : node_(node) {} 70 71 template <typename T, ListNode<T> (T::*M)> 72 T* ListHead<T, M>::Iterator::operator*() const { 73 return ContainerOf(M, node_); 74 } 75 76 template <typename T, ListNode<T> (T::*M)> 77 const typename ListHead<T, M>::Iterator& 78 ListHead<T, M>::Iterator::operator++() { 79 node_ = node_->next_; 80 return *this; 81 } 82 83 template <typename T, ListNode<T> (T::*M)> 84 bool ListHead<T, M>::Iterator::operator!=(const Iterator& that) const { 85 return node_ != that.node_; 86 } 87 88 template <typename T, ListNode<T> (T::*M)> 89 ListHead<T, M>::~ListHead() { 90 while (IsEmpty() == false) 91 head_.next_->Remove(); 92 } 93 94 template <typename T, ListNode<T> (T::*M)> 95 void ListHead<T, M>::PushBack(T* element) { 96 ListNode<T>* that = &(element->*M); 97 head_.prev_->next_ = that; 98 that->prev_ = head_.prev_; 99 that->next_ = &head_; 100 head_.prev_ = that; 101 } 102 103 template <typename T, ListNode<T> (T::*M)> 104 void ListHead<T, M>::PushFront(T* element) { 105 ListNode<T>* that = &(element->*M); 106 head_.next_->prev_ = that; 107 that->prev_ = &head_; 108 that->next_ = head_.next_; 109 head_.next_ = that; 110 } 111 112 template <typename T, ListNode<T> (T::*M)> 113 bool ListHead<T, M>::IsEmpty() const { 114 return head_.IsEmpty(); 115 } 116 117 template <typename T, ListNode<T> (T::*M)> 118 T* ListHead<T, M>::PopFront() { 119 if (IsEmpty()) 120 return nullptr; 121 ListNode<T>* node = head_.next_; 122 node->Remove(); 123 return ContainerOf(M, node); 124 } 125 126 template <typename T, ListNode<T> (T::*M)> 127 typename ListHead<T, M>::Iterator ListHead<T, M>::begin() const { 128 return Iterator(head_.next_); 129 } 130 131 template <typename T, ListNode<T> (T::*M)> 132 typename ListHead<T, M>::Iterator ListHead<T, M>::end() const { 133 return Iterator(const_cast<ListNode<T>*>(&head_)); 134 } 135 136 template <typename Inner, typename Outer> 137 constexpr uintptr_t OffsetOf(Inner Outer::*field) { 138 return reinterpret_cast<uintptr_t>(&(static_cast<Outer*>(nullptr)->*field)); 139 } 140 141 template <typename Inner, typename Outer> 142 ContainerOfHelper<Inner, Outer>::ContainerOfHelper(Inner Outer::*field, 143 Inner* pointer) 144 : pointer_( 145 reinterpret_cast<Outer*>( 146 reinterpret_cast<uintptr_t>(pointer) - OffsetOf(field))) {} 147 148 template <typename Inner, typename Outer> 149 template <typename TypeName> 150 ContainerOfHelper<Inner, Outer>::operator TypeName*() const { 151 return static_cast<TypeName*>(pointer_); 152 } 153 154 template <typename Inner, typename Outer> 155 constexpr ContainerOfHelper<Inner, Outer> ContainerOf(Inner Outer::*field, 156 Inner* pointer) { 157 return ContainerOfHelper<Inner, Outer>(field, pointer); 158 } 159 160 inline v8::Local<v8::String> OneByteString(v8::Isolate* isolate, 161 const char* data, 162 int length, 163 v8::NewStringType type) { 164 return v8::String::NewFromOneByte( 165 isolate, reinterpret_cast<const uint8_t*>(data), type, length) 166 .ToLocalChecked(); 167 } 168 169 inline v8::Local<v8::String> OneByteString(v8::Isolate* isolate, 170 const signed char* data, 171 int length, 172 v8::NewStringType type) { 173 return v8::String::NewFromOneByte( 174 isolate, reinterpret_cast<const uint8_t*>(data), type, length) 175 .ToLocalChecked(); 176 } 177 178 inline v8::Local<v8::String> OneByteString(v8::Isolate* isolate, 179 const unsigned char* data, 180 int length, 181 v8::NewStringType type) { 182 return v8::String::NewFromOneByte(isolate, data, type, length) 183 .ToLocalChecked(); 184 } 185 186 inline v8::Local<v8::String> OneByteString(v8::Isolate* isolate, 187 std::string_view str, 188 v8::NewStringType type) { 189 return OneByteString(isolate, str.data(), str.size(), type); 190 } 191 192 char ToLower(char c) { 193 return std::tolower(c, std::locale::classic()); 194 } 195 196 template <typename T> 197 std::string ToLower(const T& in) { 198 auto it = std::cbegin(in); 199 auto end = std::cend(in); 200 std::string out(std::distance(it, end), 0); 201 size_t i; 202 for (i = 0; it != end; ++it, ++i) out[i] = ToLower(*it); 203 DCHECK_EQ(i, out.size()); 204 return out; 205 } 206 207 char ToUpper(char c) { 208 return std::toupper(c, std::locale::classic()); 209 } 210 211 template <typename T> 212 std::string ToUpper(const T& in) { 213 auto it = std::cbegin(in); 214 auto end = std::cend(in); 215 std::string out(std::distance(it, end), 0); 216 size_t i; 217 for (i = 0; it != end; ++it, ++i) out[i] = ToUpper(*it); 218 DCHECK_EQ(i, out.size()); 219 return out; 220 } 221 222 bool StringEqualNoCase(const char* a, const char* b) { 223 while (ToLower(*a) == ToLower(*b++)) { 224 if (*a++ == '\0') 225 return true; 226 } 227 return false; 228 } 229 230 bool StringEqualNoCaseN(const char* a, const char* b, size_t length) { 231 for (size_t i = 0; i < length; i++) { 232 if (ToLower(a[i]) != ToLower(b[i])) 233 return false; 234 if (a[i] == '\0') 235 return true; 236 } 237 return true; 238 } 239 240 template <typename T> 241 inline T MultiplyWithOverflowCheck(T a, T b) { 242 auto ret = a * b; 243 if (a != 0) 244 CHECK_EQ(b, ret / a); 245 246 return ret; 247 } 248 249 // These should be used in our code as opposed to the native 250 // versions as they abstract out some platform and or 251 // compiler version specific functionality. 252 // malloc(0) and realloc(ptr, 0) have implementation-defined behavior in 253 // that the standard allows them to either return a unique pointer or a 254 // nullptr for zero-sized allocation requests. Normalize by always using 255 // a nullptr. 256 template <typename T> 257 T* UncheckedRealloc(T* pointer, size_t n) { 258 size_t full_size = MultiplyWithOverflowCheck(sizeof(T), n); 259 260 if (full_size == 0) { 261 free(pointer); 262 return nullptr; 263 } 264 265 void* allocated = realloc(pointer, full_size); 266 267 if (allocated == nullptr) [[unlikely]] { 268 // Tell V8 that memory is low and retry. 269 LowMemoryNotification(); 270 allocated = realloc(pointer, full_size); 271 } 272 273 return static_cast<T*>(allocated); 274 } 275 276 // As per spec realloc behaves like malloc if passed nullptr. 277 template <typename T> 278 inline T* UncheckedMalloc(size_t n) { 279 return UncheckedRealloc<T>(nullptr, n); 280 } 281 282 template <typename T> 283 inline T* UncheckedCalloc(size_t n) { 284 if (MultiplyWithOverflowCheck(sizeof(T), n) == 0) return nullptr; 285 return static_cast<T*>(calloc(n, sizeof(T))); 286 } 287 288 template <typename T> 289 inline T* Realloc(T* pointer, size_t n) { 290 T* ret = UncheckedRealloc(pointer, n); 291 CHECK_IMPLIES(n > 0, ret != nullptr); 292 return ret; 293 } 294 295 template <typename T> 296 inline T* Malloc(size_t n) { 297 T* ret = UncheckedMalloc<T>(n); 298 CHECK_IMPLIES(n > 0, ret != nullptr); 299 return ret; 300 } 301 302 template <typename T> 303 inline T* Calloc(size_t n) { 304 T* ret = UncheckedCalloc<T>(n); 305 CHECK_IMPLIES(n > 0, ret != nullptr); 306 return ret; 307 } 308 309 // Shortcuts for char*. 310 inline char* Malloc(size_t n) { return Malloc<char>(n); } 311 inline char* Calloc(size_t n) { return Calloc<char>(n); } 312 inline char* UncheckedMalloc(size_t n) { return UncheckedMalloc<char>(n); } 313 inline char* UncheckedCalloc(size_t n) { return UncheckedCalloc<char>(n); } 314 315 // This is a helper in the .cc file so including util-inl.h doesn't include more 316 // headers than we really need to. 317 void ThrowErrStringTooLong(v8::Isolate* isolate); 318 319 struct ArrayIterationData { 320 std::vector<v8::Global<v8::Value>>* out; 321 v8::Isolate* isolate = nullptr; 322 }; 323 324 inline v8::Array::CallbackResult PushItemToVector(uint32_t index, 325 v8::Local<v8::Value> element, 326 void* data) { 327 auto vec = static_cast<ArrayIterationData*>(data)->out; 328 auto isolate = static_cast<ArrayIterationData*>(data)->isolate; 329 vec->push_back(v8::Global<v8::Value>(isolate, element)); 330 return v8::Array::CallbackResult::kContinue; 331 } 332 333 v8::Maybe<void> FromV8Array(v8::Local<v8::Context> context, 334 v8::Local<v8::Array> js_array, 335 std::vector<v8::Global<v8::Value>>* out) { 336 uint32_t count = js_array->Length(); 337 out->reserve(count); 338 ArrayIterationData data{out, context->GetIsolate()}; 339 return js_array->Iterate(context, PushItemToVector, &data); 340 } 341 342 v8::MaybeLocal<v8::Value> ToV8Value(v8::Local<v8::Context> context, 343 std::string_view str, 344 v8::Isolate* isolate) { 345 if (isolate == nullptr) isolate = context->GetIsolate(); 346 if (str.size() >= static_cast<size_t>(v8::String::kMaxLength)) [[unlikely]] { 347 // V8 only has a TODO comment about adding an exception when the maximum 348 // string size is exceeded. 349 ThrowErrStringTooLong(isolate); 350 return v8::MaybeLocal<v8::Value>(); 351 } 352 353 return v8::String::NewFromUtf8( 354 isolate, str.data(), v8::NewStringType::kNormal, str.size()) 355 .FromMaybe(v8::Local<v8::String>()); 356 } 357 358 v8::MaybeLocal<v8::Value> ToV8Value(v8::Local<v8::Context> context, 359 v8_inspector::StringView str, 360 v8::Isolate* isolate) { 361 if (isolate == nullptr) isolate = context->GetIsolate(); 362 if (str.length() >= static_cast<size_t>(v8::String::kMaxLength)) 363 [[unlikely]] { 364 // V8 only has a TODO comment about adding an exception when the maximum 365 // string size is exceeded. 366 ThrowErrStringTooLong(isolate); 367 return v8::MaybeLocal<v8::Value>(); 368 } 369 370 if (str.is8Bit()) { 371 return v8::String::NewFromOneByte(isolate, 372 str.characters8(), 373 v8::NewStringType::kNormal, 374 str.length()) 375 .FromMaybe(v8::Local<v8::String>()); 376 } 377 return v8::String::NewFromTwoByte(isolate, 378 str.characters16(), 379 v8::NewStringType::kNormal, 380 str.length()) 381 .FromMaybe(v8::Local<v8::String>()); 382 } 383 384 template <typename T> 385 v8::MaybeLocal<v8::Value> ToV8Value(v8::Local<v8::Context> context, 386 const std::vector<T>& vec, 387 v8::Isolate* isolate) { 388 if (isolate == nullptr) isolate = context->GetIsolate(); 389 v8::EscapableHandleScope handle_scope(isolate); 390 391 MaybeStackBuffer<v8::Local<v8::Value>, 128> arr(vec.size()); 392 arr.SetLength(vec.size()); 393 for (size_t i = 0; i < vec.size(); ++i) { 394 if (!ToV8Value(context, vec[i], isolate).ToLocal(&arr[i])) 395 return v8::MaybeLocal<v8::Value>(); 396 } 397 398 return handle_scope.Escape(v8::Array::New(isolate, arr.out(), arr.length())); 399 } 400 401 template <typename T> 402 v8::MaybeLocal<v8::Value> ToV8Value(v8::Local<v8::Context> context, 403 const std::set<T>& set, 404 v8::Isolate* isolate) { 405 if (isolate == nullptr) isolate = context->GetIsolate(); 406 v8::Local<v8::Set> set_js = v8::Set::New(isolate); 407 v8::HandleScope handle_scope(isolate); 408 409 for (const T& entry : set) { 410 v8::Local<v8::Value> value; 411 if (!ToV8Value(context, entry, isolate).ToLocal(&value)) 412 return {}; 413 if (set_js->Add(context, value).IsEmpty()) 414 return {}; 415 } 416 417 return set_js; 418 } 419 420 template <typename T, typename U> 421 v8::MaybeLocal<v8::Value> ToV8Value(v8::Local<v8::Context> context, 422 const std::unordered_map<T, U>& map, 423 v8::Isolate* isolate) { 424 if (isolate == nullptr) isolate = context->GetIsolate(); 425 v8::EscapableHandleScope handle_scope(isolate); 426 427 v8::Local<v8::Map> ret = v8::Map::New(isolate); 428 for (const auto& item : map) { 429 v8::Local<v8::Value> first, second; 430 if (!ToV8Value(context, item.first, isolate).ToLocal(&first) || 431 !ToV8Value(context, item.second, isolate).ToLocal(&second) || 432 ret->Set(context, first, second).IsEmpty()) { 433 return v8::MaybeLocal<v8::Value>(); 434 } 435 } 436 437 return handle_scope.Escape(ret); 438 } 439 440 template <typename T> 441 v8::Local<v8::Value> ConvertNumberToV8Value(v8::Isolate* isolate, 442 const T& number) { 443 using Limits = std::numeric_limits<T>; 444 // Choose Uint32, Int32, or Double depending on range checks. 445 // These checks should all collapse at compile time. 446 if (static_cast<uint32_t>(Limits::max()) <= 447 std::numeric_limits<uint32_t>::max() && 448 static_cast<uint32_t>(Limits::min()) >= 449 std::numeric_limits<uint32_t>::min() && Limits::is_exact) { 450 return v8::Integer::NewFromUnsigned(isolate, static_cast<uint32_t>(number)); 451 } 452 453 if (static_cast<int32_t>(Limits::max()) <= 454 std::numeric_limits<int32_t>::max() && 455 static_cast<int32_t>(Limits::min()) >= 456 std::numeric_limits<int32_t>::min() && Limits::is_exact) { 457 return v8::Integer::New(isolate, static_cast<int32_t>(number)); 458 } 459 460 return v8::Number::New(isolate, static_cast<double>(number)); 461 } 462 463 template <typename T, typename> 464 v8::MaybeLocal<v8::Value> ToV8Value(v8::Local<v8::Context> context, 465 const T& number, 466 v8::Isolate* isolate) { 467 if (isolate == nullptr) isolate = context->GetIsolate(); 468 return ConvertNumberToV8Value(isolate, number); 469 } 470 471 template <typename T> 472 v8::Local<v8::Array> ToV8ValuePrimitiveArray(v8::Local<v8::Context> context, 473 const std::vector<T>& vec, 474 v8::Isolate* isolate) { 475 static_assert( 476 std::is_same_v<T, bool> || std::is_integral_v<T> || 477 std::is_floating_point_v<T>, 478 "Only primitive types (bool, integral, floating-point) are supported."); 479 480 if (isolate == nullptr) isolate = context->GetIsolate(); 481 v8::EscapableHandleScope handle_scope(isolate); 482 483 v8::LocalVector<v8::Value> elements(isolate); 484 elements.reserve(vec.size()); 485 486 for (const auto& value : vec) { 487 if constexpr (std::is_same_v<T, bool>) { 488 elements.emplace_back(v8::Boolean::New(isolate, value)); 489 } else { 490 v8::Local<v8::Value> v = ConvertNumberToV8Value(isolate, value); 491 elements.emplace_back(v); 492 } 493 } 494 495 v8::Local<v8::Array> arr = 496 v8::Array::New(isolate, elements.data(), elements.size()); 497 return handle_scope.Escape(arr); 498 } 499 500 SlicedArguments::SlicedArguments( 501 const v8::FunctionCallbackInfo<v8::Value>& args, size_t start) { 502 const size_t length = static_cast<size_t>(args.Length()); 503 if (start >= length) return; 504 const size_t size = length - start; 505 506 AllocateSufficientStorage(size); 507 for (size_t i = 0; i < size; ++i) 508 (*this)[i] = args[i + start]; 509 } 510 511 template <typename T, size_t kStackStorageSize> 512 void MaybeStackBuffer<T, kStackStorageSize>::AllocateSufficientStorage( 513 size_t storage) { 514 CHECK(!IsInvalidated()); 515 if (storage > capacity()) { 516 bool was_allocated = IsAllocated(); 517 T* allocated_ptr = was_allocated ? buf_ : nullptr; 518 buf_ = Realloc(allocated_ptr, storage); 519 capacity_ = storage; 520 if (!was_allocated && length_ > 0) 521 memcpy(buf_, buf_st_, length_ * sizeof(buf_[0])); 522 } 523 524 length_ = storage; 525 } 526 527 template <typename T, size_t S> 528 ArrayBufferViewContents<T, S>::ArrayBufferViewContents( 529 v8::Local<v8::Value> value) { 530 DCHECK(value->IsArrayBufferView() || value->IsSharedArrayBuffer() || 531 value->IsArrayBuffer()); 532 ReadValue(value); 533 } 534 535 template <typename T, size_t S> 536 ArrayBufferViewContents<T, S>::ArrayBufferViewContents( 537 v8::Local<v8::Object> value) { 538 CHECK(value->IsArrayBufferView()); 539 Read(value.As<v8::ArrayBufferView>()); 540 } 541 542 template <typename T, size_t S> 543 ArrayBufferViewContents<T, S>::ArrayBufferViewContents( 544 v8::Local<v8::ArrayBufferView> abv) { 545 Read(abv); 546 } 547 548 template <typename T, size_t S> 549 void ArrayBufferViewContents<T, S>::Read(v8::Local<v8::ArrayBufferView> abv) { 550 static_assert(sizeof(T) == 1, "Only supports one-byte data at the moment"); 551 length_ = abv->ByteLength(); 552 if (length_ > sizeof(stack_storage_) || abv->HasBuffer()) { 553 data_ = static_cast<T*>(abv->Buffer()->Data()) + abv->ByteOffset(); 554 } else { 555 abv->CopyContents(stack_storage_, sizeof(stack_storage_)); 556 data_ = stack_storage_; 557 } 558 } 559 560 template <typename T, size_t S> 561 void ArrayBufferViewContents<T, S>::ReadValue(v8::Local<v8::Value> buf) { 562 static_assert(sizeof(T) == 1, "Only supports one-byte data at the moment"); 563 DCHECK(buf->IsArrayBufferView() || buf->IsSharedArrayBuffer() || 564 buf->IsArrayBuffer()); 565 566 if (buf->IsArrayBufferView()) { 567 Read(buf.As<v8::ArrayBufferView>()); 568 } else if (buf->IsArrayBuffer()) { 569 auto ab = buf.As<v8::ArrayBuffer>(); 570 length_ = ab->ByteLength(); 571 data_ = static_cast<T*>(ab->Data()); 572 was_detached_ = ab->WasDetached(); 573 } else { 574 CHECK(buf->IsSharedArrayBuffer()); 575 auto sab = buf.As<v8::SharedArrayBuffer>(); 576 length_ = sab->ByteLength(); 577 data_ = static_cast<T*>(sab->Data()); 578 } 579 } 580 581 // ECMA-262, 15th edition, 21.1.2.5. Number.isSafeInteger 582 inline bool IsSafeJsInt(v8::Local<v8::Value> v) { 583 if (!v->IsNumber()) return false; 584 double v_d = v.As<v8::Number>()->Value(); 585 if (std::isnan(v_d)) return false; 586 if (std::isinf(v_d)) return false; 587 if (std::trunc(v_d) != v_d) return false; // not int 588 if (std::abs(v_d) <= static_cast<double>(kMaxSafeJsInteger)) return true; 589 return false; 590 } 591 592 constexpr size_t FastStringKey::HashImpl(std::string_view str) { 593 // Low-quality hash (djb2), but just fine for current use cases. 594 size_t h = 5381; 595 for (const char c : str) { 596 h = h * 33 + c; 597 } 598 return h; 599 } 600 601 constexpr size_t FastStringKey::Hash::operator()( 602 const FastStringKey& key) const { 603 return key.cached_hash_; 604 } 605 606 constexpr bool FastStringKey::operator==(const FastStringKey& other) const { 607 return name_ == other.name_; 608 } 609 610 consteval FastStringKey::FastStringKey(std::string_view name) 611 : FastStringKey(name, 0) {} 612 613 constexpr FastStringKey FastStringKey::AllowDynamic(std::string_view name) { 614 return FastStringKey(name, 0); 615 } 616 617 constexpr FastStringKey::FastStringKey(std::string_view name, int dummy) 618 : name_(name), cached_hash_(HashImpl(name)) {} 619 620 constexpr std::string_view FastStringKey::as_string_view() const { 621 return name_; 622 } 623 624 // Converts a V8 numeric value to a corresponding C++ primitive or enum type. 625 template <typename T, 626 bool loose = false, 627 typename = std::enable_if_t<std::numeric_limits<T>::is_specialized || 628 std::is_enum_v<T>>> 629 T FromV8Value(v8::Local<v8::Value> value) { 630 if constexpr (std::is_enum_v<T>) { 631 using Underlying = std::underlying_type_t<T>; 632 return static_cast<T>(FromV8Value<Underlying, loose>(value)); 633 } else if constexpr (std::is_integral_v<T> && std::is_unsigned_v<T>) { 634 static_assert( 635 std::numeric_limits<T>::max() <= std::numeric_limits<uint32_t>::max() && 636 std::numeric_limits<T>::min() >= 637 std::numeric_limits<uint32_t>::min(), 638 "Type is out of unsigned integer range"); 639 if constexpr (!loose) { 640 CHECK(value->IsUint32()); 641 } else { 642 CHECK(value->IsNumber()); 643 } 644 return static_cast<T>(value.As<v8::Uint32>()->Value()); 645 } else if constexpr (std::is_integral_v<T> && std::is_signed_v<T>) { 646 static_assert( 647 std::numeric_limits<T>::max() <= std::numeric_limits<int32_t>::max() && 648 std::numeric_limits<T>::min() >= 649 std::numeric_limits<int32_t>::min(), 650 "Type is out of signed integer range"); 651 if constexpr (!loose) { 652 CHECK(value->IsInt32()); 653 } else { 654 CHECK(value->IsNumber()); 655 } 656 return static_cast<T>(value.As<v8::Int32>()->Value()); 657 } else { 658 static_assert(std::is_floating_point_v<T>, 659 "Type must be arithmetic or enum."); 660 CHECK(value->IsNumber()); 661 return static_cast<T>(value.As<v8::Number>()->Value()); 662 } 663 } 664 665 #ifdef _WIN32 666 inline bool IsWindowsBatchFile(const char* filename) { 667 std::string file_with_extension = filename; 668 // Regex to match the last extension part after the last dot, ignoring 669 // trailing spaces and dots 670 std::regex extension_regex(R"(\.([a-zA-Z0-9]+)\s*[\.\s]*$)"); 671 std::smatch match; 672 std::string extension; 673 674 if (std::regex_search(file_with_extension, match, extension_regex)) { 675 extension = ToLower(match[1].str()); 676 } 677 678 return !extension.empty() && (extension == "cmd" || extension == "bat"); 679 } 680 681 inline std::wstring ConvertToWideString(const std::string& str, 682 UINT code_page) { 683 int size_needed = MultiByteToWideChar( 684 code_page, 0, &str[0], static_cast<int>(str.size()), nullptr, 0); 685 std::wstring wstrTo(size_needed, 0); 686 MultiByteToWideChar(code_page, 687 0, 688 &str[0], 689 static_cast<int>(str.size()), 690 &wstrTo[0], 691 size_needed); 692 return wstrTo; 693 } 694 #endif // _WIN32 695 696 } // namespace node 697 698 #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS 699 700 #endif // SRC_UTIL_INL_H_