1 | //===-- Memory Size ---------------------------------------------*- C++ -*-===// |
---|---|
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #ifndef LLVM_LIBC_SRC___SUPPORT_MEMORY_SIZE_H |
10 | #define LLVM_LIBC_SRC___SUPPORT_MEMORY_SIZE_H |
11 | |
12 | #include "src/__support/CPP/bit.h" // has_single_bit |
13 | #include "src/__support/CPP/limits.h" |
14 | #include "src/__support/CPP/type_traits.h" |
15 | #include "src/__support/macros/attributes.h" |
16 | #include "src/__support/macros/optimization.h" |
17 | #include "src/string/memory_utils/utils.h" |
18 | |
19 | namespace LIBC_NAMESPACE { |
20 | namespace internal { |
21 | template <class T> LIBC_INLINE bool mul_overflow(T a, T b, T *res) { |
22 | #if __has_builtin(__builtin_mul_overflow) |
23 | return __builtin_mul_overflow(a, b, res); |
24 | #else |
25 | T max = cpp::numeric_limits<T>::max(); |
26 | T min = cpp::numeric_limits<T>::min(); |
27 | bool overflow = (b > 0 && (a > max / b || a < min / b)) || |
28 | (b < 0 && (a < max / b || a > min / b)); |
29 | if (!overflow) |
30 | *res = a * b; |
31 | return overflow; |
32 | #endif |
33 | } |
34 | // Limit memory size to the max of ssize_t |
35 | class SafeMemSize { |
36 | private: |
37 | using type = cpp::make_signed_t<size_t>; |
38 | type value; |
39 | LIBC_INLINE explicit SafeMemSize(type value) : value(value) {} |
40 | |
41 | public: |
42 | LIBC_INLINE_VAR static constexpr size_t MAX_MEM_SIZE = |
43 | static_cast<size_t>(cpp::numeric_limits<type>::max()); |
44 | |
45 | LIBC_INLINE explicit SafeMemSize(size_t value) |
46 | : value(value <= MAX_MEM_SIZE ? static_cast<type>(value) : -1) {} |
47 | |
48 | LIBC_INLINE static constexpr size_t offset_to(size_t val, size_t align) { |
49 | return (-val) & (align - 1); |
50 | } |
51 | |
52 | LIBC_INLINE operator size_t() { return static_cast<size_t>(value); } |
53 | |
54 | LIBC_INLINE bool valid() { return value >= 0; } |
55 | |
56 | LIBC_INLINE SafeMemSize operator+(const SafeMemSize &other) { |
57 | type result; |
58 | if (LIBC_UNLIKELY((value | other.value) < 0)) { |
59 | result = -1; |
60 | } else { |
61 | result = value + other.value; |
62 | } |
63 | return SafeMemSize{result}; |
64 | } |
65 | |
66 | LIBC_INLINE SafeMemSize operator*(const SafeMemSize &other) { |
67 | type result; |
68 | if (LIBC_UNLIKELY((value | other.value) < 0)) |
69 | result = -1; |
70 | if (LIBC_UNLIKELY(mul_overflow(value, other.value, &result))) |
71 | result = -1; |
72 | return SafeMemSize{result}; |
73 | } |
74 | |
75 | LIBC_INLINE SafeMemSize align_up(size_t alignment) { |
76 | if (!cpp::has_single_bit(value: alignment) || alignment > MAX_MEM_SIZE || !valid()) |
77 | return SafeMemSize{type{-1}}; |
78 | |
79 | type offset = offset_to(val: value, align: alignment); |
80 | |
81 | if (LIBC_UNLIKELY(offset > static_cast<type>(MAX_MEM_SIZE) - value)) |
82 | return SafeMemSize{type{-1}}; |
83 | |
84 | return SafeMemSize{value + offset}; |
85 | } |
86 | }; |
87 | } // namespace internal |
88 | } // namespace LIBC_NAMESPACE |
89 | |
90 | #endif // LLVM_LIBC_SRC___SUPPORT_MEMORY_SIZE_H |
91 |