1 /*
2  * mptBaseUtils.h
3  * --------------
4  * Purpose: Various useful utility functions.
5  * Notes  : (currently none)
6  * Authors: OpenMPT Devs
7  * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
8  */
9 
10 
11 #pragma once
12 
13 #include "openmpt/all/BuildSettings.hpp"
14 
15 
16 #include "mpt/base/algorithm.hpp"
17 #include "mpt/base/arithmetic_shift.hpp"
18 #include "mpt/base/array.hpp"
19 #include "mpt/base/bit.hpp"
20 #include "mpt/base/constexpr_throw.hpp"
21 #include "mpt/base/math.hpp"
22 #include "mpt/base/memory.hpp"
23 #include "mpt/base/numeric.hpp"
24 #include "mpt/base/saturate_cast.hpp"
25 #include "mpt/base/saturate_round.hpp"
26 #include "mpt/base/utility.hpp"
27 #include "mpt/base/wrapping_divide.hpp"
28 
29 #include "mptBaseMacros.h"
30 #include "mptBaseTypes.h"
31 
32 #include <algorithm>
33 #include <limits>
34 #include <numeric>
35 #include <utility>
36 
37 #include <cmath>
38 #include <cstdlib>
39 
40 #include <math.h>
41 #include <stdlib.h>
42 
43 #if MPT_COMPILER_MSVC
44 #include <intrin.h>
45 #endif
46 
47 
48 
49 OPENMPT_NAMESPACE_BEGIN
50 
51 
52 
53 template <typename T>
Clear(T & x)54 inline void Clear(T& x)
55 {
56 	static_assert(!std::is_pointer<T>::value);
57 	mpt::reset(x);
58 }
59 
60 
61 // Memset given object to zero.
62 template <class T>
MemsetZero(T & a)63 inline void MemsetZero(T& a)
64 {
65 	static_assert(std::is_pointer<T>::value == false, "Won't memset pointers.");
66 	mpt::memclear(a);
67 }
68 
69 
70 
71 // Limits 'val' to given range. If 'val' is less than 'lowerLimit', 'val' is set to value 'lowerLimit'.
72 // Similarly if 'val' is greater than 'upperLimit', 'val' is set to value 'upperLimit'.
73 // If 'lowerLimit' > 'upperLimit', 'val' won't be modified.
74 template<class T, class C>
Limit(T & val,const C lowerLimit,const C upperLimit)75 inline void Limit(T& val, const C lowerLimit, const C upperLimit)
76 {
77 	if(lowerLimit > upperLimit) return;
78 	if(val < lowerLimit) val = lowerLimit;
79 	else if(val > upperLimit) val = upperLimit;
80 }
81 
82 
83 // Like Limit, but returns value
84 template<class T, class C>
Clamp(T val,const C lowerLimit,const C upperLimit)85 inline T Clamp(T val, const C lowerLimit, const C upperLimit)
86 {
87 	if(val < lowerLimit) return lowerLimit;
88 	else if(val > upperLimit) return upperLimit;
89 	else return val;
90 }
91 
92 // Like Limit, but with upperlimit only.
93 template<class T, class C>
LimitMax(T & val,const C upperLimit)94 inline void LimitMax(T& val, const C upperLimit)
95 {
96 	if(val > upperLimit)
97 		val = upperLimit;
98 }
99 
100 
101 
102 namespace Util
103 {
104 
105 	// Returns maximum value of given integer type.
MaxValueOfType(const T &)106 	template <class T> constexpr T MaxValueOfType(const T&) {static_assert(std::numeric_limits<T>::is_integer == true, "Only integer types are allowed."); return (std::numeric_limits<T>::max)();}
107 
108 }  // namespace Util
109 
110 
111 
112 namespace Util {
113 
114 	// Multiply two 32-bit integers, receive 64-bit result.
115 	// MSVC generates unnecessarily complicated code for the unoptimized variant using _allmul.
mul32to64(int32 a,int32 b)116 	MPT_CONSTEXPR20_FUN int64 mul32to64(int32 a, int32 b)
117 	{
118 		#if MPT_COMPILER_MSVC && (defined(_M_IX86) || defined(_M_X64))
119 			MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20())
120 			{
121 				return static_cast<int64>(a) * b;
122 			} else
123 			{
124 				return __emul(a, b);
125 			}
126 		#else
127 			return static_cast<int64>(a) * b;
128 		#endif
129 	}
130 
mul32to64_unsigned(uint32 a,uint32 b)131 	MPT_CONSTEXPR20_FUN uint64 mul32to64_unsigned(uint32 a, uint32 b)
132 	{
133 		#if MPT_COMPILER_MSVC && (defined(_M_IX86) || defined(_M_X64))
134 			MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20())
135 			{
136 				return static_cast<uint64>(a) * b;
137 			} else
138 			{
139 				return __emulu(a, b);
140 			}
141 		#else
142 			return static_cast<uint64>(a) * b;
143 		#endif
144 	}
145 
muldiv(int32 a,int32 b,int32 c)146 	MPT_CONSTEXPR20_FUN int32 muldiv(int32 a, int32 b, int32 c)
147 	{
148 		return mpt::saturate_cast<int32>( mul32to64( a, b ) / c );
149 	}
150 
muldivr(int32 a,int32 b,int32 c)151 	MPT_CONSTEXPR20_FUN int32 muldivr(int32 a, int32 b, int32 c)
152 	{
153 		return mpt::saturate_cast<int32>( ( mul32to64( a, b ) + ( c / 2 ) ) / c );
154 	}
155 
156 	// Do not use overloading because catching unsigned version by accident results in slower X86 code.
muldiv_unsigned(uint32 a,uint32 b,uint32 c)157 	MPT_CONSTEXPR20_FUN uint32 muldiv_unsigned(uint32 a, uint32 b, uint32 c)
158 	{
159 		return mpt::saturate_cast<uint32>( mul32to64_unsigned( a, b ) / c );
160 	}
muldivr_unsigned(uint32 a,uint32 b,uint32 c)161 	MPT_CONSTEXPR20_FUN uint32 muldivr_unsigned(uint32 a, uint32 b, uint32 c)
162 	{
163 		return mpt::saturate_cast<uint32>( ( mul32to64_unsigned( a, b ) + ( c / 2u ) ) / c );
164 	}
165 
muldivrfloor(int64 a,uint32 b,uint32 c)166 	constexpr MPT_FORCEINLINE int32 muldivrfloor(int64 a, uint32 b, uint32 c)
167 	{
168 		a *= b;
169 		a += c / 2u;
170 		return (a >= 0) ? mpt::saturate_cast<int32>(a / c) : mpt::saturate_cast<int32>((a - (c - 1)) / c);
171 	}
172 
173 } // namespace Util
174 
175 
176 
177 OPENMPT_NAMESPACE_END
178