xref: /aosp_15_r20/external/cronet/base/numerics/basic_ops_impl.h (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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 #ifndef BASE_NUMERICS_BASIC_OPS_IMPL_H_
6 #define BASE_NUMERICS_BASIC_OPS_IMPL_H_
7 
8 #include <bit>
9 #include <cstdint>
10 #include <cstdlib>
11 #include <cstring>
12 #include <span>
13 #include <type_traits>
14 
15 namespace base::internal {
16 
17 // The correct type to perform math operations on given values of type `T`. This
18 // may be a larger type than `T` to avoid promotion to `int` which involves sign
19 // conversion!
20 template <class T>
21   requires(std::is_integral_v<T>)
22 using MathType = std::conditional_t<
23     sizeof(T) >= sizeof(int),
24     T,
25     std::conditional_t<std::is_signed_v<T>, int, unsigned int>>;
26 
27 // Reverses the byte order of the integer.
28 template <class T>
requires(std::is_unsigned_v<T> && std::is_integral_v<T>)29   requires(std::is_unsigned_v<T> && std::is_integral_v<T>)
30 inline constexpr T SwapBytes(T value) {
31   // MSVC intrinsics are not constexpr, so we provide our own constexpr
32   // implementation. We provide it unconditionally so we can test it on all
33   // platforms for correctness.
34   if (std::is_constant_evaluated()) {
35     if constexpr (sizeof(T) == 1u) {
36       return value;
37     } else if constexpr (sizeof(T) == 2u) {
38       MathType<T> a = (MathType<T>(value) >> 0) & MathType<T>{0xff};
39       MathType<T> b = (MathType<T>(value) >> 8) & MathType<T>{0xff};
40       return static_cast<T>((a << 8) | (b << 0));
41     } else if constexpr (sizeof(T) == 4u) {
42       T a = (value >> 0) & T{0xff};
43       T b = (value >> 8) & T{0xff};
44       T c = (value >> 16) & T{0xff};
45       T d = (value >> 24) & T{0xff};
46       return (a << 24) | (b << 16) | (c << 8) | (d << 0);
47     } else {
48       static_assert(sizeof(T) == 8u);
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       T e = (value >> 32) & T{0xff};
54       T f = (value >> 40) & T{0xff};
55       T g = (value >> 48) & T{0xff};
56       T h = (value >> 56) & T{0xff};
57       return (a << 56) | (b << 48) | (c << 40) | (d << 32) |  //
58              (e << 24) | (f << 16) | (g << 8) | (h << 0);
59     }
60   }
61 
62 #if _MSC_VER
63   if constexpr (sizeof(T) == 1u) {
64     return value;
65   } else if constexpr (sizeof(T) == sizeof(unsigned short)) {
66     using U = unsigned short;
67     return _byteswap_ushort(U{value});
68   } else if constexpr (sizeof(T) == sizeof(unsigned long)) {
69     using U = unsigned long;
70     return _byteswap_ulong(U{value});
71   } else {
72     static_assert(sizeof(T) == 8u);
73     return _byteswap_uint64(value);
74   }
75 #else
76   if constexpr (sizeof(T) == 1u) {
77     return value;
78   } else if constexpr (sizeof(T) == 2u) {
79     return __builtin_bswap16(uint16_t{value});
80   } else if constexpr (sizeof(T) == 4u) {
81     return __builtin_bswap32(value);
82   } else {
83     static_assert(sizeof(T) == 8u);
84     return __builtin_bswap64(value);
85   }
86 #endif
87 }
88 
89 // Signed values are byte-swapped as unsigned values.
90 template <class T>
requires(std::is_signed_v<T> && std::is_integral_v<T>)91   requires(std::is_signed_v<T> && std::is_integral_v<T>)
92 inline constexpr T SwapBytes(T value) {
93   return static_cast<T>(SwapBytes(static_cast<std::make_unsigned_t<T>>(value)));
94 }
95 
96 // Converts from a byte array to an integer.
97 template <class T>
requires(std::is_unsigned_v<T> && std::is_integral_v<T>)98   requires(std::is_unsigned_v<T> && std::is_integral_v<T>)
99 inline constexpr T FromLittleEndian(std::span<const uint8_t, sizeof(T)> bytes) {
100   T val;
101   if (std::is_constant_evaluated()) {
102     val = T{0};
103     for (size_t i = 0u; i < sizeof(T); i += 1u) {
104       // SAFETY: `i < sizeof(T)` (the number of bytes in T), so `(8 * i)` is
105       // less than the number of bits in T.
106       val |= MathType<T>(bytes[i]) << (8u * i);
107     }
108   } else {
109     // SAFETY: `bytes` has sizeof(T) bytes, and `val` is of type `T` so has
110     // sizeof(T) bytes, and the two can not alias as `val` is a stack variable.
111     memcpy(&val, bytes.data(), sizeof(T));
112   }
113   return val;
114 }
115 
116 template <class T>
requires(std::is_signed_v<T> && std::is_integral_v<T>)117   requires(std::is_signed_v<T> && std::is_integral_v<T>)
118 inline constexpr T FromLittleEndian(std::span<const uint8_t, sizeof(T)> bytes) {
119   return static_cast<T>(FromLittleEndian<std::make_unsigned_t<T>>(bytes));
120 }
121 
122 // Converts to a byte array from an integer.
123 template <class T>
requires(std::is_unsigned_v<T> && std::is_integral_v<T>)124   requires(std::is_unsigned_v<T> && std::is_integral_v<T>)
125 inline constexpr std::array<uint8_t, sizeof(T)> ToLittleEndian(T val) {
126   auto bytes = std::array<uint8_t, sizeof(T)>();
127   if (std::is_constant_evaluated()) {
128     for (size_t i = 0u; i < sizeof(T); i += 1u) {
129       const auto last_byte = static_cast<uint8_t>(val & 0xff);
130       // The low bytes go to the front of the array in little endian.
131       bytes[i] = last_byte;
132       // If `val` is one byte, this shift would be UB. But it's also not needed
133       // since the loop will not run again.
134       if constexpr (sizeof(T) > 1u) {
135         val >>= 8u;
136       }
137     }
138   } else {
139     // SAFETY: `bytes` has sizeof(T) bytes, and `val` is of type `T` so has
140     // sizeof(T) bytes, and the two can not alias as `val` is a stack variable.
141     memcpy(bytes.data(), &val, sizeof(T));
142   }
143   return bytes;
144 }
145 
146 template <class T>
requires(std::is_signed_v<T> && std::is_integral_v<T>)147   requires(std::is_signed_v<T> && std::is_integral_v<T>)
148 inline constexpr std::array<uint8_t, sizeof(T)> ToLittleEndian(T val) {
149   return ToLittleEndian(static_cast<std::make_unsigned_t<T>>(val));
150 }
151 }  // namespace base::internal
152 
153 #endif  //  BASE_NUMERICS_BASIC_OPS_IMPL_H_
154