v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
basic_ops_impl.h
Go to the documentation of this file.
1// Copyright 2024 The Chromium Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5// Slightly adapted for inclusion in V8.
6// Copyright 2025 the V8 project authors. All rights reserved.
7
8#ifdef UNSAFE_BUFFERS_BUILD
9// TODO(crbug.com/390223051): Remove C-library calls to fix the errors.
10#pragma allow_unsafe_libc_calls
11#endif
12
13#ifndef V8_BASE_NUMERICS_BASIC_OPS_IMPL_H_
14#define V8_BASE_NUMERICS_BASIC_OPS_IMPL_H_
15
16#include <array>
17#include <cstdint>
18#include <cstdlib>
19#include <cstring>
20#include <span>
21#include <type_traits>
22
24
25// The correct type to perform math operations on given values of type `T`. This
26// may be a larger type than `T` to avoid promotion to `int` which involves sign
27// conversion!
28template <class T>
29 requires(std::is_integral_v<T>)
30using MathType = std::conditional_t<
31 sizeof(T) >= sizeof(int), T,
32 std::conditional_t<std::is_signed_v<T>, int, unsigned int>>;
33
34// Reverses the byte order of the integer.
35template <class T>
36 requires(std::is_unsigned_v<T> && std::is_integral_v<T>)
37inline constexpr T SwapBytes(T value) {
38 // MSVC intrinsics are not constexpr, so we provide our own constexpr
39 // implementation. We provide it unconditionally so we can test it on all
40 // platforms for correctness.
41 if (std::is_constant_evaluated()) {
42 if constexpr (sizeof(T) == 1u) {
43 return value;
44 } else if constexpr (sizeof(T) == 2u) {
45 MathType<T> a = (MathType<T>(value) >> 0) & MathType<T>{0xff};
46 MathType<T> b = (MathType<T>(value) >> 8) & MathType<T>{0xff};
47 return static_cast<T>((a << 8) | (b << 0));
48 } else if constexpr (sizeof(T) == 4u) {
49 T a = (value >> 0) & T{0xff};
50 T b = (value >> 8) & T{0xff};
51 T c = (value >> 16) & T{0xff};
52 T d = (value >> 24) & T{0xff};
53 return (a << 24) | (b << 16) | (c << 8) | (d << 0);
54 } else {
55 static_assert(sizeof(T) == 8u);
56 T a = (value >> 0) & T{0xff};
57 T b = (value >> 8) & T{0xff};
58 T c = (value >> 16) & T{0xff};
59 T d = (value >> 24) & T{0xff};
60 T e = (value >> 32) & T{0xff};
61 T f = (value >> 40) & T{0xff};
62 T g = (value >> 48) & T{0xff};
63 T h = (value >> 56) & T{0xff};
64 return (a << 56) | (b << 48) | (c << 40) | (d << 32) | //
65 (e << 24) | (f << 16) | (g << 8) | (h << 0);
66 }
67 }
68
69#if _MSC_VER
70 if constexpr (sizeof(T) == 1u) {
71 return value;
72 // NOLINTNEXTLINE(runtime/int)
73 } else if constexpr (sizeof(T) == sizeof(unsigned short)) {
74 using U = unsigned short; // NOLINT(runtime/int)
75 return _byteswap_ushort(U{value});
76 // NOLINTNEXTLINE(runtime/int)
77 } else if constexpr (sizeof(T) == sizeof(unsigned long)) {
78 using U = unsigned long; // NOLINT(runtime/int)
79 return _byteswap_ulong(U{value});
80 } else {
81 static_assert(sizeof(T) == 8u);
82 return _byteswap_uint64(value);
83 }
84#else
85 if constexpr (sizeof(T) == 1u) {
86 return value;
87 } else if constexpr (sizeof(T) == 2u) {
88 return __builtin_bswap16(uint16_t{value});
89 } else if constexpr (sizeof(T) == 4u) {
90 return __builtin_bswap32(value);
91 } else {
92 static_assert(sizeof(T) == 8u);
93 return __builtin_bswap64(value);
94 }
95#endif
96}
97
98// Signed values are byte-swapped as unsigned values.
99template <class T>
100 requires(std::is_signed_v<T> && std::is_integral_v<T>)
101inline constexpr T SwapBytes(T value) {
102 return static_cast<T>(SwapBytes(static_cast<std::make_unsigned_t<T>>(value)));
103}
104
105// Converts from a byte array to an integer.
106template <class T>
107 requires(std::is_unsigned_v<T> && std::is_integral_v<T>)
108inline constexpr T FromLittleEndian(std::span<const uint8_t, sizeof(T)> bytes) {
109 T val;
110 if (std::is_constant_evaluated()) {
111 val = T{0};
112 for (size_t i = 0u; i < sizeof(T); i += 1u) {
113 // SAFETY: `i < sizeof(T)` (the number of bytes in T), so `(8 * i)` is
114 // less than the number of bits in T.
115 val |= MathType<T>(bytes[i]) << (8u * i);
116 }
117 } else {
118 // SAFETY: `bytes` has sizeof(T) bytes, and `val` is of type `T` so has
119 // sizeof(T) bytes, and the two can not alias as `val` is a stack variable.
120 memcpy(&val, bytes.data(), sizeof(T));
121 }
122 return val;
123}
124
125template <class T>
126 requires(std::is_signed_v<T> && std::is_integral_v<T>)
127inline constexpr T FromLittleEndian(std::span<const uint8_t, sizeof(T)> bytes) {
128 return static_cast<T>(FromLittleEndian<std::make_unsigned_t<T>>(bytes));
129}
130
131// Converts to a byte array from an integer.
132template <class T>
133 requires(std::is_unsigned_v<T> && std::is_integral_v<T>)
134inline constexpr std::array<uint8_t, sizeof(T)> ToLittleEndian(T val) {
135 auto bytes = std::array<uint8_t, sizeof(T)>();
136 if (std::is_constant_evaluated()) {
137 for (size_t i = 0u; i < sizeof(T); i += 1u) {
138 const auto last_byte = static_cast<uint8_t>(val & 0xff);
139 // The low bytes go to the front of the array in little endian.
140 bytes[i] = last_byte;
141 // If `val` is one byte, this shift would be UB. But it's also not needed
142 // since the loop will not run again.
143 if constexpr (sizeof(T) > 1u) {
144 val >>= 8u;
145 }
146 }
147 } else {
148 // SAFETY: `bytes` has sizeof(T) bytes, and `val` is of type `T` so has
149 // sizeof(T) bytes, and the two can not alias as `val` is a stack variable.
150 memcpy(bytes.data(), &val, sizeof(T));
151 }
152 return bytes;
153}
154
155template <class T>
156 requires(std::is_signed_v<T> && std::is_integral_v<T>)
157inline constexpr std::array<uint8_t, sizeof(T)> ToLittleEndian(T val) {
158 return ToLittleEndian(static_cast<std::make_unsigned_t<T>>(val));
159}
160} // namespace v8::base::internal
161
162#endif // V8_BASE_NUMERICS_BASIC_OPS_IMPL_H_
#define T
std::conditional_t< sizeof(T) >=sizeof(int), T, std::conditional_t< std::is_signed_v< T >, int, unsigned int > > MathType
constexpr T SwapBytes(T value)
constexpr T FromLittleEndian(std::span< const uint8_t, sizeof(T)> bytes)
constexpr std::array< uint8_t, sizeof(T)> ToLittleEndian(T val)
std::unique_ptr< ValueMirror > value