зеркало из https://github.com/microsoft/terminal.git
Introduce chromium safe math (#4144)
## Summary of the Pull Request <!-- Please review the items on the PR checklist before submitting--> ## PR Checklist * [x] Closes #4013 * [x] I work here. * [x] Existing tests should be OK. Real changes, just adding a lib to use. * [x] Couldn't find any existing docs about intsafe. * [x] Am core contributor. ## Detailed Description of the Pull Request / Additional comments * [x] Can we remove min/max completely or rename it in the two projects where it had to be reintroduced? This is now moved into #4152 * [x] How many usages of the old safe math are there? **79** * [x] If not a ton, can we migrate them here or in a follow on PR? This is now moved into #4153 Files with old safe math: - TerminalControl: TSFInputControl.cpp - TerminalCore: TerminalDispatch.cpp - TerminalCore: TerminalSelection.cpp - Host: directio.cpp - RendererGdi: invalidate.cpp - RendererGdi: math.cpp - RendererGdi: paint.cpp - RendererVt: paint.cpp - TerminalAdapter: adaptDispatch.cpp - Types: viewport.cpp - Types: WindowUiaProviderBase.cpp ## Validation Steps Performed
This commit is contained in:
Родитель
6d6fb7f690
Коммит
4d1c7cf3eb
36
NOTICE.md
36
NOTICE.md
|
@ -77,3 +77,39 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## chromium/base/numerics
|
||||||
|
|
||||||
|
**Source**:
|
||||||
|
|
||||||
|
### License
|
||||||
|
|
||||||
|
```
|
||||||
|
Copyright 2015 The Chromium Authors. All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above
|
||||||
|
copyright notice, this list of conditions and the following disclaimer
|
||||||
|
in the documentation and/or other materials provided with the
|
||||||
|
distribution.
|
||||||
|
* Neither the name of Google Inc. nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
```
|
|
@ -0,0 +1,27 @@
|
||||||
|
// Copyright 2015 The Chromium Authors. All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -0,0 +1,17 @@
|
||||||
|
### Notes for Future Maintainers
|
||||||
|
|
||||||
|
This was originally imported by @miniksa in January 2020.
|
||||||
|
|
||||||
|
The provenance information (where it came from and which commit) is stored in the file `cgmanifest.json` in the same directory as this readme.
|
||||||
|
Please update the provenance information in that file when ingesting an updated version of the dependent library.
|
||||||
|
That provenance file is automatically read and inventoried by Microsoft systems to ensure compliance with appropiate governance standards.
|
||||||
|
|
||||||
|
## What should be done to update this in the future?
|
||||||
|
|
||||||
|
1. Go to chromium/chromium repository on GitHub.
|
||||||
|
2. Take the entire contents of the base/numerics directory wholesale and drop it in the base/numerics directory here.
|
||||||
|
3. Don't change anything about it.
|
||||||
|
4. Validate that the license in the root of the repository didn't change and update it if so. It is sitting in the same directory as this readme.
|
||||||
|
If it changed dramatically, ensure that it is still compatible with our license scheme. Also update the NOTICE file in the root of our repository to declare the third-party usage.
|
||||||
|
5. Submit the pull.
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
# Copyright (c) 2017 The Chromium Authors. All rights reserved.
|
||||||
|
# Use of this source code is governed by a BSD-style license that can be
|
||||||
|
# found in the LICENSE file.
|
||||||
|
|
||||||
|
# This is a dependency-free, header-only, library, and it needs to stay that
|
||||||
|
# way to facilitate pulling it into various third-party projects. So, this
|
||||||
|
# file is here to protect against accidentally introducing external
|
||||||
|
# dependencies or depending on internal implementation details.
|
||||||
|
source_set("base_numerics") {
|
||||||
|
visibility = [ "//base/*" ]
|
||||||
|
sources = [
|
||||||
|
"checked_math_impl.h",
|
||||||
|
"clamped_math_impl.h",
|
||||||
|
"safe_conversions_arm_impl.h",
|
||||||
|
"safe_conversions_impl.h",
|
||||||
|
"safe_math_arm_impl.h",
|
||||||
|
"safe_math_clang_gcc_impl.h",
|
||||||
|
"safe_math_shared_impl.h",
|
||||||
|
]
|
||||||
|
public = [
|
||||||
|
"checked_math.h",
|
||||||
|
"clamped_math.h",
|
||||||
|
"math_constants.h",
|
||||||
|
"ranges.h",
|
||||||
|
"safe_conversions.h",
|
||||||
|
"safe_math.h",
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
# This is a dependency-free, header-only, library, and it needs to stay that
|
||||||
|
# way to facilitate pulling it into various third-party projects. So, this
|
||||||
|
# file is here to protect against accidentally introducing dependencies.
|
||||||
|
include_rules = [
|
||||||
|
"-base",
|
||||||
|
"+base/numerics",
|
||||||
|
]
|
|
@ -0,0 +1,5 @@
|
||||||
|
jschuh@chromium.org
|
||||||
|
tsepez@chromium.org
|
||||||
|
|
||||||
|
|
||||||
|
# COMPONENT: Internals
|
|
@ -0,0 +1,409 @@
|
||||||
|
# `base/numerics`
|
||||||
|
|
||||||
|
This directory contains a dependency-free, header-only library of templates
|
||||||
|
providing well-defined semantics for safely and performantly handling a variety
|
||||||
|
of numeric operations, including most common arithmetic operations and
|
||||||
|
conversions.
|
||||||
|
|
||||||
|
The public API is broken out into the following header files:
|
||||||
|
|
||||||
|
* `checked_math.h` contains the `CheckedNumeric` template class and helper
|
||||||
|
functions for performing arithmetic and conversion operations that detect
|
||||||
|
errors and boundary conditions (e.g. overflow, truncation, etc.).
|
||||||
|
* `clamped_math.h` contains the `ClampedNumeric` template class and
|
||||||
|
helper functions for performing fast, clamped (i.e. [non-sticky](#notsticky)
|
||||||
|
saturating) arithmetic operations and conversions.
|
||||||
|
* `safe_conversions.h` contains the `StrictNumeric` template class and
|
||||||
|
a collection of custom casting templates and helper functions for safely
|
||||||
|
converting between a range of numeric types.
|
||||||
|
* `safe_math.h` includes all of the previously mentioned headers.
|
||||||
|
|
||||||
|
*** aside
|
||||||
|
**Note:** The `Numeric` template types implicitly convert from C numeric types
|
||||||
|
and `Numeric` templates that are convertable to an underlying C numeric type.
|
||||||
|
The conversion priority for `Numeric` type coercions is:
|
||||||
|
|
||||||
|
* `StrictNumeric` coerces to `ClampedNumeric` and `CheckedNumeric`
|
||||||
|
* `ClampedNumeric` coerces to `CheckedNumeric`
|
||||||
|
***
|
||||||
|
|
||||||
|
[TOC]
|
||||||
|
|
||||||
|
## Common patterns and use-cases
|
||||||
|
|
||||||
|
The following covers the preferred style for the most common uses of this
|
||||||
|
library. Please don't cargo-cult from anywhere else. 😉
|
||||||
|
|
||||||
|
### Performing checked arithmetic type conversions
|
||||||
|
|
||||||
|
The `checked_cast` template converts between arbitrary arithmetic types, and is
|
||||||
|
used for cases where a conversion failure should result in program termination:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// Crash if signed_value is out of range for buff_size.
|
||||||
|
size_t buff_size = checked_cast<size_t>(signed_value);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Performing saturated (clamped) arithmetic type conversions
|
||||||
|
|
||||||
|
The `saturated_cast` template converts between arbitrary arithmetic types, and
|
||||||
|
is used in cases where an out-of-bounds source value should be saturated to the
|
||||||
|
corresponding maximum or minimum of the destination type:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// Convert from float with saturation to INT_MAX, INT_MIN, or 0 for NaN.
|
||||||
|
int int_value = saturated_cast<int>(floating_point_value);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Enforcing arithmetic type conversions at compile-time
|
||||||
|
|
||||||
|
The `strict_cast` emits code that is identical to `static_cast`. However,
|
||||||
|
provides static checks that will cause a compilation failure if the
|
||||||
|
destination type cannot represent the full range of the source type:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// Throw a compiler error if byte_value is changed to an out-of-range-type.
|
||||||
|
int int_value = strict_cast<int>(byte_value);
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also enforce these compile-time restrictions on function parameters by
|
||||||
|
using the `StrictNumeric` template:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// Throw a compiler error if the size argument cannot be represented by a
|
||||||
|
// size_t (e.g. passing an int will fail to compile).
|
||||||
|
bool AllocateBuffer(void** buffer, StrictCast<size_t> size);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Comparing values between arbitrary arithmetic types
|
||||||
|
|
||||||
|
Both the `StrictNumeric` and `ClampedNumeric` types provide well defined
|
||||||
|
comparisons between arbitrary arithmetic types. This allows you to perform
|
||||||
|
comparisons that are not legal or would trigger compiler warnings or errors
|
||||||
|
under the normal arithmetic promotion rules:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
bool foo(unsigned value, int upper_bound) {
|
||||||
|
// Converting to StrictNumeric allows this comparison to work correctly.
|
||||||
|
if (MakeStrictNum(value) >= upper_bound)
|
||||||
|
return false;
|
||||||
|
```
|
||||||
|
|
||||||
|
*** note
|
||||||
|
**Warning:** Do not perform manual conversions using the comparison operators.
|
||||||
|
Instead, use the cast templates described in the previous sections, or the
|
||||||
|
constexpr template functions `IsValueInRangeForNumericType` and
|
||||||
|
`IsTypeInRangeForNumericType`, as these templates properly handle the full range
|
||||||
|
of corner cases and employ various optimizations.
|
||||||
|
***
|
||||||
|
|
||||||
|
### Calculating a buffer size (checked arithmetic)
|
||||||
|
|
||||||
|
When making exact calculations—such as for buffer lengths—it's often necessary
|
||||||
|
to know when those calculations trigger an overflow, undefined behavior, or
|
||||||
|
other boundary conditions. The `CheckedNumeric` template does this by storing
|
||||||
|
a bit determining whether or not some arithmetic operation has occured that
|
||||||
|
would put the variable in an "invalid" state. Attempting to extract the value
|
||||||
|
from a variable in an invalid state will trigger a check/trap condition, that
|
||||||
|
by default will result in process termination.
|
||||||
|
|
||||||
|
Here's an example of a buffer calculation using a `CheckedNumeric` type (note:
|
||||||
|
the AssignIfValid method will trigger a compile error if the result is ignored).
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// Calculate the buffer size and detect if an overflow occurs.
|
||||||
|
size_t size;
|
||||||
|
if (!CheckAdd(kHeaderSize, CheckMul(count, kItemSize)).AssignIfValid(&size)) {
|
||||||
|
// Handle an overflow error...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Calculating clamped coordinates (non-sticky saturating arithmetic)
|
||||||
|
|
||||||
|
Certain classes of calculations—such as coordinate calculations—require
|
||||||
|
well-defined semantics that always produce a valid result on boundary
|
||||||
|
conditions. The `ClampedNumeric` template addresses this by providing
|
||||||
|
performant, non-sticky saturating arithmetic operations.
|
||||||
|
|
||||||
|
Here's an example of using a `ClampedNumeric` to calculate an operation
|
||||||
|
insetting a rectangle.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// Use clamped arithmetic since inset calculations might overflow.
|
||||||
|
void Rect::Inset(int left, int top, int right, int bottom) {
|
||||||
|
origin_ += Vector2d(left, top);
|
||||||
|
set_width(ClampSub(width(), ClampAdd(left, right)));
|
||||||
|
set_height(ClampSub(height(), ClampAdd(top, bottom)));
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
*** note
|
||||||
|
<a name="notsticky"></a>
|
||||||
|
The `ClampedNumeric` type is not "sticky", which means the saturation is not
|
||||||
|
retained across individual operations. As such, one arithmetic operation may
|
||||||
|
result in a saturated value, while the next operation may then "desaturate"
|
||||||
|
the value. Here's an example:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
ClampedNumeric<int> value = INT_MAX;
|
||||||
|
++value; // value is still INT_MAX, due to saturation.
|
||||||
|
--value; // value is now (INT_MAX - 1), because saturation is not sticky.
|
||||||
|
```
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
## Conversion functions and StrictNumeric<> in safe_conversions.h
|
||||||
|
|
||||||
|
This header includes a collection of helper `constexpr` templates for safely
|
||||||
|
performing a range of conversions, assignments, and tests.
|
||||||
|
|
||||||
|
### Safe casting templates
|
||||||
|
|
||||||
|
* `as_signed()` - Returns the supplied integral value as a signed type of
|
||||||
|
the same width.
|
||||||
|
* `as_unsigned()` - Returns the supplied integral value as an unsigned type
|
||||||
|
of the same width.
|
||||||
|
* `checked_cast<>()` - Analogous to `static_cast<>` for numeric types, except
|
||||||
|
that by default it will trigger a crash on an out-of-bounds conversion (e.g.
|
||||||
|
overflow, underflow, NaN to integral) or a compile error if the conversion
|
||||||
|
error can be detected at compile time. The crash handler can be overridden
|
||||||
|
to perform a behavior other than crashing.
|
||||||
|
* `saturated_cast<>()` - Analogous to `static_cast` for numeric types, except
|
||||||
|
that it returns a saturated result when the specified numeric conversion
|
||||||
|
would otherwise overflow or underflow. An NaN source returns 0 by
|
||||||
|
default, but can be overridden to return a different result.
|
||||||
|
* `strict_cast<>()` - Analogous to `static_cast` for numeric types, except
|
||||||
|
this causes a compile failure if the destination type is not large
|
||||||
|
enough to contain any value in the source type. It performs no runtime
|
||||||
|
checking and thus introduces no runtime overhead.
|
||||||
|
|
||||||
|
### Other helper and conversion functions
|
||||||
|
|
||||||
|
* `IsValueInRangeForNumericType<>()` - A convenience function that returns
|
||||||
|
true if the type supplied as the template parameter can represent the value
|
||||||
|
passed as an argument to the function.
|
||||||
|
* `IsTypeInRangeForNumericType<>()` - A convenience function that evaluates
|
||||||
|
entirely at compile-time and returns true if the destination type (first
|
||||||
|
template parameter) can represent the full range of the source type
|
||||||
|
(second template parameter).
|
||||||
|
* `IsValueNegative()` - A convenience function that will accept any
|
||||||
|
arithmetic type as an argument and will return whether the value is less
|
||||||
|
than zero. Unsigned types always return false.
|
||||||
|
* `SafeUnsignedAbs()` - Returns the absolute value of the supplied integer
|
||||||
|
parameter as an unsigned result (thus avoiding an overflow if the value
|
||||||
|
is the signed, two's complement minimum).
|
||||||
|
|
||||||
|
### StrictNumeric<>
|
||||||
|
|
||||||
|
`StrictNumeric<>` is a wrapper type that performs assignments and copies via
|
||||||
|
the `strict_cast` template, and can perform valid arithmetic comparisons
|
||||||
|
across any range of arithmetic types. `StrictNumeric` is the return type for
|
||||||
|
values extracted from a `CheckedNumeric` class instance. The raw numeric value
|
||||||
|
is extracted via `static_cast` to the underlying type or any type with
|
||||||
|
sufficient range to represent the underlying type.
|
||||||
|
|
||||||
|
* `MakeStrictNum()` - Creates a new `StrictNumeric` from the underlying type
|
||||||
|
of the supplied arithmetic or StrictNumeric type.
|
||||||
|
* `SizeT` - Alias for `StrictNumeric<size_t>`.
|
||||||
|
|
||||||
|
## CheckedNumeric<> in checked_math.h
|
||||||
|
|
||||||
|
`CheckedNumeric<>` implements all the logic and operators for detecting integer
|
||||||
|
boundary conditions such as overflow, underflow, and invalid conversions.
|
||||||
|
The `CheckedNumeric` type implicitly converts from floating point and integer
|
||||||
|
data types, and contains overloads for basic arithmetic operations (i.e.: `+`,
|
||||||
|
`-`, `*`, `/` for all types and `%`, `<<`, `>>`, `&`, `|`, `^` for integers).
|
||||||
|
However, *the [variadic template functions
|
||||||
|
](#CheckedNumeric_in-checked_math_h-Non_member-helper-functions)
|
||||||
|
are the prefered API,* as they remove type ambiguities and help prevent a number
|
||||||
|
of common errors. The variadic functions can also be more performant, as they
|
||||||
|
eliminate redundant expressions that are unavoidable with the with the operator
|
||||||
|
overloads. (Ideally the compiler should optimize those away, but better to avoid
|
||||||
|
them in the first place.)
|
||||||
|
|
||||||
|
Type promotions are a slightly modified version of the [standard C/C++ numeric
|
||||||
|
promotions
|
||||||
|
](http://en.cppreference.com/w/cpp/language/implicit_conversion#Numeric_promotions)
|
||||||
|
with the two differences being that *there is no default promotion to int*
|
||||||
|
and *bitwise logical operations always return an unsigned of the wider type.*
|
||||||
|
|
||||||
|
### Members
|
||||||
|
|
||||||
|
The unary negation, increment, and decrement operators are supported, along
|
||||||
|
with the following unary arithmetic methods, which return a new
|
||||||
|
`CheckedNumeric` as a result of the operation:
|
||||||
|
|
||||||
|
* `Abs()` - Absolute value.
|
||||||
|
* `UnsignedAbs()` - Absolute value as an equal-width unsigned underlying type
|
||||||
|
(valid for only integral types).
|
||||||
|
* `Max()` - Returns whichever is greater of the current instance or argument.
|
||||||
|
The underlying return type is whichever has the greatest magnitude.
|
||||||
|
* `Min()` - Returns whichever is lowest of the current instance or argument.
|
||||||
|
The underlying return type is whichever has can represent the lowest
|
||||||
|
number in the smallest width (e.g. int8_t over unsigned, int over
|
||||||
|
int8_t, and float over int).
|
||||||
|
|
||||||
|
The following are for converting `CheckedNumeric` instances:
|
||||||
|
|
||||||
|
* `type` - The underlying numeric type.
|
||||||
|
* `AssignIfValid()` - Assigns the underlying value to the supplied
|
||||||
|
destination pointer if the value is currently valid and within the
|
||||||
|
range supported by the destination type. Returns true on success.
|
||||||
|
* `Cast<>()` - Instance method returning a `CheckedNumeric` derived from
|
||||||
|
casting the current instance to a `CheckedNumeric` of the supplied
|
||||||
|
destination type.
|
||||||
|
|
||||||
|
*** aside
|
||||||
|
The following member functions return a `StrictNumeric`, which is valid for
|
||||||
|
comparison and assignment operations, but will trigger a compile failure on
|
||||||
|
attempts to assign to a type of insufficient range. The underlying value can
|
||||||
|
be extracted by an explicit `static_cast` to the underlying type or any type
|
||||||
|
with sufficient range to represent the underlying type.
|
||||||
|
***
|
||||||
|
|
||||||
|
* `IsValid()` - Returns true if the underlying numeric value is valid (i.e.
|
||||||
|
has not wrapped or saturated and is not the result of an invalid
|
||||||
|
conversion).
|
||||||
|
* `ValueOrDie()` - Returns the underlying value. If the state is not valid
|
||||||
|
this call will trigger a crash by default (but may be overridden by
|
||||||
|
supplying an alternate handler to the template).
|
||||||
|
* `ValueOrDefault()` - Returns the current value, or the supplied default if
|
||||||
|
the state is not valid (but will not crash).
|
||||||
|
|
||||||
|
**Comparison operators are explicitly not provided** for `CheckedNumeric`
|
||||||
|
types because they could result in a crash if the type is not in a valid state.
|
||||||
|
Patterns like the following should be used instead:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// Either input or padding (or both) may be arbitrary sizes.
|
||||||
|
size_t buff_size;
|
||||||
|
if (!CheckAdd(input, padding, kHeaderLength).AssignIfValid(&buff_size) ||
|
||||||
|
buff_size >= kMaxBuffer) {
|
||||||
|
// Handle an error...
|
||||||
|
} else {
|
||||||
|
// Do stuff on success...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Non-member helper functions
|
||||||
|
|
||||||
|
The following variadic convenience functions, which accept standard arithmetic
|
||||||
|
or `CheckedNumeric` types, perform arithmetic operations, and return a
|
||||||
|
`CheckedNumeric` result. The supported functions are:
|
||||||
|
|
||||||
|
* `CheckAdd()` - Addition.
|
||||||
|
* `CheckSub()` - Subtraction.
|
||||||
|
* `CheckMul()` - Multiplication.
|
||||||
|
* `CheckDiv()` - Division.
|
||||||
|
* `CheckMod()` - Modulus (integer only).
|
||||||
|
* `CheckLsh()` - Left integer shift (integer only).
|
||||||
|
* `CheckRsh()` - Right integer shift (integer only).
|
||||||
|
* `CheckAnd()` - Bitwise AND (integer only with unsigned result).
|
||||||
|
* `CheckOr()` - Bitwise OR (integer only with unsigned result).
|
||||||
|
* `CheckXor()` - Bitwise XOR (integer only with unsigned result).
|
||||||
|
* `CheckMax()` - Maximum of supplied arguments.
|
||||||
|
* `CheckMin()` - Minimum of supplied arguments.
|
||||||
|
|
||||||
|
The following wrapper functions can be used to avoid the template
|
||||||
|
disambiguator syntax when converting a destination type.
|
||||||
|
|
||||||
|
* `IsValidForType<>()` in place of: `a.template IsValid<>()`
|
||||||
|
* `ValueOrDieForType<>()` in place of: `a.template ValueOrDie<>()`
|
||||||
|
* `ValueOrDefaultForType<>()` in place of: `a.template ValueOrDefault<>()`
|
||||||
|
|
||||||
|
The following general utility methods is are useful for converting from
|
||||||
|
arithmetic types to `CheckedNumeric` types:
|
||||||
|
|
||||||
|
* `MakeCheckedNum()` - Creates a new `CheckedNumeric` from the underlying type
|
||||||
|
of the supplied arithmetic or directly convertible type.
|
||||||
|
|
||||||
|
## ClampedNumeric<> in clamped_math.h
|
||||||
|
|
||||||
|
`ClampedNumeric<>` implements all the logic and operators for clamped
|
||||||
|
(non-sticky saturating) arithmetic operations and conversions. The
|
||||||
|
`ClampedNumeric` type implicitly converts back and forth between floating point
|
||||||
|
and integer data types, saturating on assignment as appropriate. It contains
|
||||||
|
overloads for basic arithmetic operations (i.e.: `+`, `-`, `*`, `/` for
|
||||||
|
all types and `%`, `<<`, `>>`, `&`, `|`, `^` for integers) along with comparison
|
||||||
|
operators for arithmetic types of any size. However, *the [variadic template
|
||||||
|
functions
|
||||||
|
](#ClampedNumeric_in-clamped_math_h-Non_member-helper-functions)
|
||||||
|
are the prefered API,* as they remove type ambiguities and help prevent
|
||||||
|
a number of common errors. The variadic functions can also be more performant,
|
||||||
|
as they eliminate redundant expressions that are unavoidable with the operator
|
||||||
|
overloads. (Ideally the compiler should optimize those away, but better to avoid
|
||||||
|
them in the first place.)
|
||||||
|
|
||||||
|
Type promotions are a slightly modified version of the [standard C/C++ numeric
|
||||||
|
promotions
|
||||||
|
](http://en.cppreference.com/w/cpp/language/implicit_conversion#Numeric_promotions)
|
||||||
|
with the two differences being that *there is no default promotion to int*
|
||||||
|
and *bitwise logical operations always return an unsigned of the wider type.*
|
||||||
|
|
||||||
|
*** aside
|
||||||
|
Most arithmetic operations saturate normally, to the numeric limit in the
|
||||||
|
direction of the sign. The potentially unusual cases are:
|
||||||
|
|
||||||
|
* **Division:** Division by zero returns the saturated limit in the direction
|
||||||
|
of sign of the dividend (first argument). The one exception is 0/0, which
|
||||||
|
returns zero (although logically is NaN).
|
||||||
|
* **Modulus:** Division by zero returns the dividend (first argument).
|
||||||
|
* **Left shift:** Non-zero values saturate in the direction of the signed
|
||||||
|
limit (max/min), even for shifts larger than the bit width. 0 shifted any
|
||||||
|
amount results in 0.
|
||||||
|
* **Right shift:** Negative values saturate to -1. Positive or 0 saturates
|
||||||
|
to 0. (Effectively just an unbounded arithmetic-right-shift.)
|
||||||
|
* **Bitwise operations:** No saturation; bit pattern is identical to
|
||||||
|
non-saturated bitwise operations.
|
||||||
|
***
|
||||||
|
|
||||||
|
### Members
|
||||||
|
|
||||||
|
The unary negation, increment, and decrement operators are supported, along
|
||||||
|
with the following unary arithmetic methods, which return a new
|
||||||
|
`ClampedNumeric` as a result of the operation:
|
||||||
|
|
||||||
|
* `Abs()` - Absolute value.
|
||||||
|
* `UnsignedAbs()` - Absolute value as an equal-width unsigned underlying type
|
||||||
|
(valid for only integral types).
|
||||||
|
* `Max()` - Returns whichever is greater of the current instance or argument.
|
||||||
|
The underlying return type is whichever has the greatest magnitude.
|
||||||
|
* `Min()` - Returns whichever is lowest of the current instance or argument.
|
||||||
|
The underlying return type is whichever has can represent the lowest
|
||||||
|
number in the smallest width (e.g. int8_t over unsigned, int over
|
||||||
|
int8_t, and float over int).
|
||||||
|
|
||||||
|
The following are for converting `ClampedNumeric` instances:
|
||||||
|
|
||||||
|
* `type` - The underlying numeric type.
|
||||||
|
* `RawValue()` - Returns the raw value as the underlying arithmetic type. This
|
||||||
|
is useful when e.g. assigning to an auto type or passing as a deduced
|
||||||
|
template parameter.
|
||||||
|
* `Cast<>()` - Instance method returning a `ClampedNumeric` derived from
|
||||||
|
casting the current instance to a `ClampedNumeric` of the supplied
|
||||||
|
destination type.
|
||||||
|
|
||||||
|
### Non-member helper functions
|
||||||
|
|
||||||
|
The following variadic convenience functions, which accept standard arithmetic
|
||||||
|
or `ClampedNumeric` types, perform arithmetic operations, and return a
|
||||||
|
`ClampedNumeric` result. The supported functions are:
|
||||||
|
|
||||||
|
* `ClampAdd()` - Addition.
|
||||||
|
* `ClampSub()` - Subtraction.
|
||||||
|
* `ClampMul()` - Multiplication.
|
||||||
|
* `ClampDiv()` - Division.
|
||||||
|
* `ClampMod()` - Modulus (integer only).
|
||||||
|
* `ClampLsh()` - Left integer shift (integer only).
|
||||||
|
* `ClampRsh()` - Right integer shift (integer only).
|
||||||
|
* `ClampAnd()` - Bitwise AND (integer only with unsigned result).
|
||||||
|
* `ClampOr()` - Bitwise OR (integer only with unsigned result).
|
||||||
|
* `ClampXor()` - Bitwise XOR (integer only with unsigned result).
|
||||||
|
* `ClampMax()` - Maximum of supplied arguments.
|
||||||
|
* `ClampMin()` - Minimum of supplied arguments.
|
||||||
|
|
||||||
|
The following is a general utility method that is useful for converting
|
||||||
|
to a `ClampedNumeric` type:
|
||||||
|
|
||||||
|
* `MakeClampedNum()` - Creates a new `ClampedNumeric` from the underlying type
|
||||||
|
of the supplied arithmetic or directly convertible type.
|
|
@ -0,0 +1,393 @@
|
||||||
|
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#ifndef BASE_NUMERICS_CHECKED_MATH_H_
|
||||||
|
#define BASE_NUMERICS_CHECKED_MATH_H_
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
#include "base/numerics/checked_math_impl.h"
|
||||||
|
|
||||||
|
namespace base {
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class CheckedNumeric {
|
||||||
|
static_assert(std::is_arithmetic<T>::value,
|
||||||
|
"CheckedNumeric<T>: T must be a numeric type.");
|
||||||
|
|
||||||
|
public:
|
||||||
|
using type = T;
|
||||||
|
|
||||||
|
constexpr CheckedNumeric() = default;
|
||||||
|
|
||||||
|
// Copy constructor.
|
||||||
|
template <typename Src>
|
||||||
|
constexpr CheckedNumeric(const CheckedNumeric<Src>& rhs)
|
||||||
|
: state_(rhs.state_.value(), rhs.IsValid()) {}
|
||||||
|
|
||||||
|
template <typename Src>
|
||||||
|
friend class CheckedNumeric;
|
||||||
|
|
||||||
|
// This is not an explicit constructor because we implicitly upgrade regular
|
||||||
|
// numerics to CheckedNumerics to make them easier to use.
|
||||||
|
template <typename Src>
|
||||||
|
constexpr CheckedNumeric(Src value) // NOLINT(runtime/explicit)
|
||||||
|
: state_(value) {
|
||||||
|
static_assert(std::is_arithmetic<Src>::value, "Argument must be numeric.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is not an explicit constructor because we want a seamless conversion
|
||||||
|
// from StrictNumeric types.
|
||||||
|
template <typename Src>
|
||||||
|
constexpr CheckedNumeric(
|
||||||
|
StrictNumeric<Src> value) // NOLINT(runtime/explicit)
|
||||||
|
: state_(static_cast<Src>(value)) {}
|
||||||
|
|
||||||
|
// IsValid() - The public API to test if a CheckedNumeric is currently valid.
|
||||||
|
// A range checked destination type can be supplied using the Dst template
|
||||||
|
// parameter.
|
||||||
|
template <typename Dst = T>
|
||||||
|
constexpr bool IsValid() const {
|
||||||
|
return state_.is_valid() &&
|
||||||
|
IsValueInRangeForNumericType<Dst>(state_.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssignIfValid(Dst) - Assigns the underlying value if it is currently valid
|
||||||
|
// and is within the range supported by the destination type. Returns true if
|
||||||
|
// successful and false otherwise.
|
||||||
|
template <typename Dst>
|
||||||
|
#if defined(__clang__) || defined(__GNUC__)
|
||||||
|
__attribute__((warn_unused_result))
|
||||||
|
#elif defined(_MSC_VER)
|
||||||
|
_Check_return_
|
||||||
|
#endif
|
||||||
|
constexpr bool
|
||||||
|
AssignIfValid(Dst* result) const {
|
||||||
|
return BASE_NUMERICS_LIKELY(IsValid<Dst>())
|
||||||
|
? ((*result = static_cast<Dst>(state_.value())), true)
|
||||||
|
: false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValueOrDie() - The primary accessor for the underlying value. If the
|
||||||
|
// current state is not valid it will CHECK and crash.
|
||||||
|
// A range checked destination type can be supplied using the Dst template
|
||||||
|
// parameter, which will trigger a CHECK if the value is not in bounds for
|
||||||
|
// the destination.
|
||||||
|
// The CHECK behavior can be overridden by supplying a handler as a
|
||||||
|
// template parameter, for test code, etc. However, the handler cannot access
|
||||||
|
// the underlying value, and it is not available through other means.
|
||||||
|
template <typename Dst = T, class CheckHandler = CheckOnFailure>
|
||||||
|
constexpr StrictNumeric<Dst> ValueOrDie() const {
|
||||||
|
return BASE_NUMERICS_LIKELY(IsValid<Dst>())
|
||||||
|
? static_cast<Dst>(state_.value())
|
||||||
|
: CheckHandler::template HandleFailure<Dst>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValueOrDefault(T default_value) - A convenience method that returns the
|
||||||
|
// current value if the state is valid, and the supplied default_value for
|
||||||
|
// any other state.
|
||||||
|
// A range checked destination type can be supplied using the Dst template
|
||||||
|
// parameter. WARNING: This function may fail to compile or CHECK at runtime
|
||||||
|
// if the supplied default_value is not within range of the destination type.
|
||||||
|
template <typename Dst = T, typename Src>
|
||||||
|
constexpr StrictNumeric<Dst> ValueOrDefault(const Src default_value) const {
|
||||||
|
return BASE_NUMERICS_LIKELY(IsValid<Dst>())
|
||||||
|
? static_cast<Dst>(state_.value())
|
||||||
|
: checked_cast<Dst>(default_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns a checked numeric of the specified type, cast from the current
|
||||||
|
// CheckedNumeric. If the current state is invalid or the destination cannot
|
||||||
|
// represent the result then the returned CheckedNumeric will be invalid.
|
||||||
|
template <typename Dst>
|
||||||
|
constexpr CheckedNumeric<typename UnderlyingType<Dst>::type> Cast() const {
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This friend method is available solely for providing more detailed logging
|
||||||
|
// in the the tests. Do not implement it in production code, because the
|
||||||
|
// underlying values may change at any time.
|
||||||
|
template <typename U>
|
||||||
|
friend U GetNumericValueForTest(const CheckedNumeric<U>& src);
|
||||||
|
|
||||||
|
// Prototypes for the supported arithmetic operator overloads.
|
||||||
|
template <typename Src>
|
||||||
|
constexpr CheckedNumeric& operator+=(const Src rhs);
|
||||||
|
template <typename Src>
|
||||||
|
constexpr CheckedNumeric& operator-=(const Src rhs);
|
||||||
|
template <typename Src>
|
||||||
|
constexpr CheckedNumeric& operator*=(const Src rhs);
|
||||||
|
template <typename Src>
|
||||||
|
constexpr CheckedNumeric& operator/=(const Src rhs);
|
||||||
|
template <typename Src>
|
||||||
|
constexpr CheckedNumeric& operator%=(const Src rhs);
|
||||||
|
template <typename Src>
|
||||||
|
constexpr CheckedNumeric& operator<<=(const Src rhs);
|
||||||
|
template <typename Src>
|
||||||
|
constexpr CheckedNumeric& operator>>=(const Src rhs);
|
||||||
|
template <typename Src>
|
||||||
|
constexpr CheckedNumeric& operator&=(const Src rhs);
|
||||||
|
template <typename Src>
|
||||||
|
constexpr CheckedNumeric& operator|=(const Src rhs);
|
||||||
|
template <typename Src>
|
||||||
|
constexpr CheckedNumeric& operator^=(const Src rhs);
|
||||||
|
|
||||||
|
constexpr CheckedNumeric operator-() const {
|
||||||
|
// The negation of two's complement int min is int min, so we simply
|
||||||
|
// check for that in the constexpr case.
|
||||||
|
// We use an optimized code path for a known run-time variable.
|
||||||
|
return MustTreatAsConstexpr(state_.value()) || !std::is_signed<T>::value ||
|
||||||
|
std::is_floating_point<T>::value
|
||||||
|
? CheckedNumeric<T>(
|
||||||
|
NegateWrapper(state_.value()),
|
||||||
|
IsValid() && (!std::is_signed<T>::value ||
|
||||||
|
std::is_floating_point<T>::value ||
|
||||||
|
NegateWrapper(state_.value()) !=
|
||||||
|
std::numeric_limits<T>::lowest()))
|
||||||
|
: FastRuntimeNegate();
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr CheckedNumeric operator~() const {
|
||||||
|
return CheckedNumeric<decltype(InvertWrapper(T()))>(
|
||||||
|
InvertWrapper(state_.value()), IsValid());
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr CheckedNumeric Abs() const {
|
||||||
|
return !IsValueNegative(state_.value()) ? *this : -*this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename U>
|
||||||
|
constexpr CheckedNumeric<typename MathWrapper<CheckedMaxOp, T, U>::type> Max(
|
||||||
|
const U rhs) const {
|
||||||
|
using R = typename UnderlyingType<U>::type;
|
||||||
|
using result_type = typename MathWrapper<CheckedMaxOp, T, U>::type;
|
||||||
|
// TODO(jschuh): This can be converted to the MathOp version and remain
|
||||||
|
// constexpr once we have C++14 support.
|
||||||
|
return CheckedNumeric<result_type>(
|
||||||
|
static_cast<result_type>(
|
||||||
|
IsGreater<T, R>::Test(state_.value(), Wrapper<U>::value(rhs))
|
||||||
|
? state_.value()
|
||||||
|
: Wrapper<U>::value(rhs)),
|
||||||
|
state_.is_valid() && Wrapper<U>::is_valid(rhs));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename U>
|
||||||
|
constexpr CheckedNumeric<typename MathWrapper<CheckedMinOp, T, U>::type> Min(
|
||||||
|
const U rhs) const {
|
||||||
|
using R = typename UnderlyingType<U>::type;
|
||||||
|
using result_type = typename MathWrapper<CheckedMinOp, T, U>::type;
|
||||||
|
// TODO(jschuh): This can be converted to the MathOp version and remain
|
||||||
|
// constexpr once we have C++14 support.
|
||||||
|
return CheckedNumeric<result_type>(
|
||||||
|
static_cast<result_type>(
|
||||||
|
IsLess<T, R>::Test(state_.value(), Wrapper<U>::value(rhs))
|
||||||
|
? state_.value()
|
||||||
|
: Wrapper<U>::value(rhs)),
|
||||||
|
state_.is_valid() && Wrapper<U>::is_valid(rhs));
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function is available only for integral types. It returns an unsigned
|
||||||
|
// integer of the same width as the source type, containing the absolute value
|
||||||
|
// of the source, and properly handling signed min.
|
||||||
|
constexpr CheckedNumeric<typename UnsignedOrFloatForSize<T>::type>
|
||||||
|
UnsignedAbs() const {
|
||||||
|
return CheckedNumeric<typename UnsignedOrFloatForSize<T>::type>(
|
||||||
|
SafeUnsignedAbs(state_.value()), state_.is_valid());
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr CheckedNumeric& operator++() {
|
||||||
|
*this += 1;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr CheckedNumeric operator++(int) {
|
||||||
|
CheckedNumeric value = *this;
|
||||||
|
*this += 1;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr CheckedNumeric& operator--() {
|
||||||
|
*this -= 1;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr CheckedNumeric operator--(int) {
|
||||||
|
CheckedNumeric value = *this;
|
||||||
|
*this -= 1;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// These perform the actual math operations on the CheckedNumerics.
|
||||||
|
// Binary arithmetic operations.
|
||||||
|
template <template <typename, typename, typename> class M,
|
||||||
|
typename L,
|
||||||
|
typename R>
|
||||||
|
static constexpr CheckedNumeric MathOp(const L lhs, const R rhs) {
|
||||||
|
using Math = typename MathWrapper<M, L, R>::math;
|
||||||
|
T result = 0;
|
||||||
|
bool is_valid =
|
||||||
|
Wrapper<L>::is_valid(lhs) && Wrapper<R>::is_valid(rhs) &&
|
||||||
|
Math::Do(Wrapper<L>::value(lhs), Wrapper<R>::value(rhs), &result);
|
||||||
|
return CheckedNumeric<T>(result, is_valid);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assignment arithmetic operations.
|
||||||
|
template <template <typename, typename, typename> class M, typename R>
|
||||||
|
constexpr CheckedNumeric& MathOp(const R rhs) {
|
||||||
|
using Math = typename MathWrapper<M, T, R>::math;
|
||||||
|
T result = 0; // Using T as the destination saves a range check.
|
||||||
|
bool is_valid = state_.is_valid() && Wrapper<R>::is_valid(rhs) &&
|
||||||
|
Math::Do(state_.value(), Wrapper<R>::value(rhs), &result);
|
||||||
|
*this = CheckedNumeric<T>(result, is_valid);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
CheckedNumericState<T> state_;
|
||||||
|
|
||||||
|
CheckedNumeric FastRuntimeNegate() const {
|
||||||
|
T result;
|
||||||
|
bool success = CheckedSubOp<T, T>::Do(T(0), state_.value(), &result);
|
||||||
|
return CheckedNumeric<T>(result, IsValid() && success);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Src>
|
||||||
|
constexpr CheckedNumeric(Src value, bool is_valid)
|
||||||
|
: state_(value, is_valid) {}
|
||||||
|
|
||||||
|
// These wrappers allow us to handle state the same way for both
|
||||||
|
// CheckedNumeric and POD arithmetic types.
|
||||||
|
template <typename Src>
|
||||||
|
struct Wrapper {
|
||||||
|
static constexpr bool is_valid(Src) { return true; }
|
||||||
|
static constexpr Src value(Src value) { return value; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Src>
|
||||||
|
struct Wrapper<CheckedNumeric<Src>> {
|
||||||
|
static constexpr bool is_valid(const CheckedNumeric<Src> v) {
|
||||||
|
return v.IsValid();
|
||||||
|
}
|
||||||
|
static constexpr Src value(const CheckedNumeric<Src> v) {
|
||||||
|
return v.state_.value();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Src>
|
||||||
|
struct Wrapper<StrictNumeric<Src>> {
|
||||||
|
static constexpr bool is_valid(const StrictNumeric<Src>) { return true; }
|
||||||
|
static constexpr Src value(const StrictNumeric<Src> v) {
|
||||||
|
return static_cast<Src>(v);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Convenience functions to avoid the ugly template disambiguator syntax.
|
||||||
|
template <typename Dst, typename Src>
|
||||||
|
constexpr bool IsValidForType(const CheckedNumeric<Src> value) {
|
||||||
|
return value.template IsValid<Dst>();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Dst, typename Src>
|
||||||
|
constexpr StrictNumeric<Dst> ValueOrDieForType(
|
||||||
|
const CheckedNumeric<Src> value) {
|
||||||
|
return value.template ValueOrDie<Dst>();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Dst, typename Src, typename Default>
|
||||||
|
constexpr StrictNumeric<Dst> ValueOrDefaultForType(
|
||||||
|
const CheckedNumeric<Src> value,
|
||||||
|
const Default default_value) {
|
||||||
|
return value.template ValueOrDefault<Dst>(default_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convience wrapper to return a new CheckedNumeric from the provided arithmetic
|
||||||
|
// or CheckedNumericType.
|
||||||
|
template <typename T>
|
||||||
|
constexpr CheckedNumeric<typename UnderlyingType<T>::type> MakeCheckedNum(
|
||||||
|
const T value) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// These implement the variadic wrapper for the math operations.
|
||||||
|
template <template <typename, typename, typename> class M,
|
||||||
|
typename L,
|
||||||
|
typename R>
|
||||||
|
constexpr CheckedNumeric<typename MathWrapper<M, L, R>::type> CheckMathOp(
|
||||||
|
const L lhs,
|
||||||
|
const R rhs) {
|
||||||
|
using Math = typename MathWrapper<M, L, R>::math;
|
||||||
|
return CheckedNumeric<typename Math::result_type>::template MathOp<M>(lhs,
|
||||||
|
rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
// General purpose wrapper template for arithmetic operations.
|
||||||
|
template <template <typename, typename, typename> class M,
|
||||||
|
typename L,
|
||||||
|
typename R,
|
||||||
|
typename... Args>
|
||||||
|
constexpr CheckedNumeric<typename ResultType<M, L, R, Args...>::type>
|
||||||
|
CheckMathOp(const L lhs, const R rhs, const Args... args) {
|
||||||
|
return CheckMathOp<M>(CheckMathOp<M>(lhs, rhs), args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Add, +, +=)
|
||||||
|
BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Sub, -, -=)
|
||||||
|
BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Mul, *, *=)
|
||||||
|
BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Div, /, /=)
|
||||||
|
BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Mod, %, %=)
|
||||||
|
BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Lsh, <<, <<=)
|
||||||
|
BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Rsh, >>, >>=)
|
||||||
|
BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, And, &, &=)
|
||||||
|
BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Or, |, |=)
|
||||||
|
BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Xor, ^, ^=)
|
||||||
|
BASE_NUMERIC_ARITHMETIC_VARIADIC(Checked, Check, Max)
|
||||||
|
BASE_NUMERIC_ARITHMETIC_VARIADIC(Checked, Check, Min)
|
||||||
|
|
||||||
|
// These are some extra StrictNumeric operators to support simple pointer
|
||||||
|
// arithmetic with our result types. Since wrapping on a pointer is always
|
||||||
|
// bad, we trigger the CHECK condition here.
|
||||||
|
template <typename L, typename R>
|
||||||
|
L* operator+(L* lhs, const StrictNumeric<R> rhs) {
|
||||||
|
uintptr_t result = CheckAdd(reinterpret_cast<uintptr_t>(lhs),
|
||||||
|
CheckMul(sizeof(L), static_cast<R>(rhs)))
|
||||||
|
.template ValueOrDie<uintptr_t>();
|
||||||
|
return reinterpret_cast<L*>(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename L, typename R>
|
||||||
|
L* operator-(L* lhs, const StrictNumeric<R> rhs) {
|
||||||
|
uintptr_t result = CheckSub(reinterpret_cast<uintptr_t>(lhs),
|
||||||
|
CheckMul(sizeof(L), static_cast<R>(rhs)))
|
||||||
|
.template ValueOrDie<uintptr_t>();
|
||||||
|
return reinterpret_cast<L*>(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
using internal::CheckedNumeric;
|
||||||
|
using internal::IsValidForType;
|
||||||
|
using internal::ValueOrDieForType;
|
||||||
|
using internal::ValueOrDefaultForType;
|
||||||
|
using internal::MakeCheckedNum;
|
||||||
|
using internal::CheckMax;
|
||||||
|
using internal::CheckMin;
|
||||||
|
using internal::CheckAdd;
|
||||||
|
using internal::CheckSub;
|
||||||
|
using internal::CheckMul;
|
||||||
|
using internal::CheckDiv;
|
||||||
|
using internal::CheckMod;
|
||||||
|
using internal::CheckLsh;
|
||||||
|
using internal::CheckRsh;
|
||||||
|
using internal::CheckAnd;
|
||||||
|
using internal::CheckOr;
|
||||||
|
using internal::CheckXor;
|
||||||
|
|
||||||
|
} // namespace base
|
||||||
|
|
||||||
|
#endif // BASE_NUMERICS_CHECKED_MATH_H_
|
|
@ -0,0 +1,567 @@
|
||||||
|
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#ifndef BASE_NUMERICS_CHECKED_MATH_IMPL_H_
|
||||||
|
#define BASE_NUMERICS_CHECKED_MATH_IMPL_H_
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <climits>
|
||||||
|
#include <cmath>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <limits>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
#include "base/numerics/safe_conversions.h"
|
||||||
|
#include "base/numerics/safe_math_shared_impl.h"
|
||||||
|
|
||||||
|
namespace base {
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr bool CheckedAddImpl(T x, T y, T* result) {
|
||||||
|
static_assert(std::is_integral<T>::value, "Type must be integral");
|
||||||
|
// Since the value of x+y is undefined if we have a signed type, we compute
|
||||||
|
// it using the unsigned type of the same size.
|
||||||
|
using UnsignedDst = typename std::make_unsigned<T>::type;
|
||||||
|
using SignedDst = typename std::make_signed<T>::type;
|
||||||
|
UnsignedDst ux = static_cast<UnsignedDst>(x);
|
||||||
|
UnsignedDst uy = static_cast<UnsignedDst>(y);
|
||||||
|
UnsignedDst uresult = static_cast<UnsignedDst>(ux + uy);
|
||||||
|
*result = static_cast<T>(uresult);
|
||||||
|
// Addition is valid if the sign of (x + y) is equal to either that of x or
|
||||||
|
// that of y.
|
||||||
|
return (std::is_signed<T>::value)
|
||||||
|
? static_cast<SignedDst>((uresult ^ ux) & (uresult ^ uy)) >= 0
|
||||||
|
: uresult >= uy; // Unsigned is either valid or underflow.
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename U, class Enable = void>
|
||||||
|
struct CheckedAddOp {};
|
||||||
|
|
||||||
|
template <typename T, typename U>
|
||||||
|
struct CheckedAddOp<T,
|
||||||
|
U,
|
||||||
|
typename std::enable_if<std::is_integral<T>::value &&
|
||||||
|
std::is_integral<U>::value>::type> {
|
||||||
|
using result_type = typename MaxExponentPromotion<T, U>::type;
|
||||||
|
template <typename V>
|
||||||
|
static constexpr bool Do(T x, U y, V* result) {
|
||||||
|
// TODO(jschuh) Make this "constexpr if" once we're C++17.
|
||||||
|
if (CheckedAddFastOp<T, U>::is_supported)
|
||||||
|
return CheckedAddFastOp<T, U>::Do(x, y, result);
|
||||||
|
|
||||||
|
// Double the underlying type up to a full machine word.
|
||||||
|
using FastPromotion = typename FastIntegerArithmeticPromotion<T, U>::type;
|
||||||
|
using Promotion =
|
||||||
|
typename std::conditional<(IntegerBitsPlusSign<FastPromotion>::value >
|
||||||
|
IntegerBitsPlusSign<intptr_t>::value),
|
||||||
|
typename BigEnoughPromotion<T, U>::type,
|
||||||
|
FastPromotion>::type;
|
||||||
|
// Fail if either operand is out of range for the promoted type.
|
||||||
|
// TODO(jschuh): This could be made to work for a broader range of values.
|
||||||
|
if (BASE_NUMERICS_UNLIKELY(!IsValueInRangeForNumericType<Promotion>(x) ||
|
||||||
|
!IsValueInRangeForNumericType<Promotion>(y))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Promotion presult = {};
|
||||||
|
bool is_valid = true;
|
||||||
|
if (IsIntegerArithmeticSafe<Promotion, T, U>::value) {
|
||||||
|
presult = static_cast<Promotion>(x) + static_cast<Promotion>(y);
|
||||||
|
} else {
|
||||||
|
is_valid = CheckedAddImpl(static_cast<Promotion>(x),
|
||||||
|
static_cast<Promotion>(y), &presult);
|
||||||
|
}
|
||||||
|
*result = static_cast<V>(presult);
|
||||||
|
return is_valid && IsValueInRangeForNumericType<V>(presult);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr bool CheckedSubImpl(T x, T y, T* result) {
|
||||||
|
static_assert(std::is_integral<T>::value, "Type must be integral");
|
||||||
|
// Since the value of x+y is undefined if we have a signed type, we compute
|
||||||
|
// it using the unsigned type of the same size.
|
||||||
|
using UnsignedDst = typename std::make_unsigned<T>::type;
|
||||||
|
using SignedDst = typename std::make_signed<T>::type;
|
||||||
|
UnsignedDst ux = static_cast<UnsignedDst>(x);
|
||||||
|
UnsignedDst uy = static_cast<UnsignedDst>(y);
|
||||||
|
UnsignedDst uresult = static_cast<UnsignedDst>(ux - uy);
|
||||||
|
*result = static_cast<T>(uresult);
|
||||||
|
// Subtraction is valid if either x and y have same sign, or (x-y) and x have
|
||||||
|
// the same sign.
|
||||||
|
return (std::is_signed<T>::value)
|
||||||
|
? static_cast<SignedDst>((uresult ^ ux) & (ux ^ uy)) >= 0
|
||||||
|
: x >= y;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename U, class Enable = void>
|
||||||
|
struct CheckedSubOp {};
|
||||||
|
|
||||||
|
template <typename T, typename U>
|
||||||
|
struct CheckedSubOp<T,
|
||||||
|
U,
|
||||||
|
typename std::enable_if<std::is_integral<T>::value &&
|
||||||
|
std::is_integral<U>::value>::type> {
|
||||||
|
using result_type = typename MaxExponentPromotion<T, U>::type;
|
||||||
|
template <typename V>
|
||||||
|
static constexpr bool Do(T x, U y, V* result) {
|
||||||
|
// TODO(jschuh) Make this "constexpr if" once we're C++17.
|
||||||
|
if (CheckedSubFastOp<T, U>::is_supported)
|
||||||
|
return CheckedSubFastOp<T, U>::Do(x, y, result);
|
||||||
|
|
||||||
|
// Double the underlying type up to a full machine word.
|
||||||
|
using FastPromotion = typename FastIntegerArithmeticPromotion<T, U>::type;
|
||||||
|
using Promotion =
|
||||||
|
typename std::conditional<(IntegerBitsPlusSign<FastPromotion>::value >
|
||||||
|
IntegerBitsPlusSign<intptr_t>::value),
|
||||||
|
typename BigEnoughPromotion<T, U>::type,
|
||||||
|
FastPromotion>::type;
|
||||||
|
// Fail if either operand is out of range for the promoted type.
|
||||||
|
// TODO(jschuh): This could be made to work for a broader range of values.
|
||||||
|
if (BASE_NUMERICS_UNLIKELY(!IsValueInRangeForNumericType<Promotion>(x) ||
|
||||||
|
!IsValueInRangeForNumericType<Promotion>(y))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Promotion presult = {};
|
||||||
|
bool is_valid = true;
|
||||||
|
if (IsIntegerArithmeticSafe<Promotion, T, U>::value) {
|
||||||
|
presult = static_cast<Promotion>(x) - static_cast<Promotion>(y);
|
||||||
|
} else {
|
||||||
|
is_valid = CheckedSubImpl(static_cast<Promotion>(x),
|
||||||
|
static_cast<Promotion>(y), &presult);
|
||||||
|
}
|
||||||
|
*result = static_cast<V>(presult);
|
||||||
|
return is_valid && IsValueInRangeForNumericType<V>(presult);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr bool CheckedMulImpl(T x, T y, T* result) {
|
||||||
|
static_assert(std::is_integral<T>::value, "Type must be integral");
|
||||||
|
// Since the value of x*y is potentially undefined if we have a signed type,
|
||||||
|
// we compute it using the unsigned type of the same size.
|
||||||
|
using UnsignedDst = typename std::make_unsigned<T>::type;
|
||||||
|
using SignedDst = typename std::make_signed<T>::type;
|
||||||
|
const UnsignedDst ux = SafeUnsignedAbs(x);
|
||||||
|
const UnsignedDst uy = SafeUnsignedAbs(y);
|
||||||
|
UnsignedDst uresult = static_cast<UnsignedDst>(ux * uy);
|
||||||
|
const bool is_negative =
|
||||||
|
std::is_signed<T>::value && static_cast<SignedDst>(x ^ y) < 0;
|
||||||
|
*result = is_negative ? 0 - uresult : uresult;
|
||||||
|
// We have a fast out for unsigned identity or zero on the second operand.
|
||||||
|
// After that it's an unsigned overflow check on the absolute value, with
|
||||||
|
// a +1 bound for a negative result.
|
||||||
|
return uy <= UnsignedDst(!std::is_signed<T>::value || is_negative) ||
|
||||||
|
ux <= (std::numeric_limits<T>::max() + UnsignedDst(is_negative)) / uy;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename U, class Enable = void>
|
||||||
|
struct CheckedMulOp {};
|
||||||
|
|
||||||
|
template <typename T, typename U>
|
||||||
|
struct CheckedMulOp<T,
|
||||||
|
U,
|
||||||
|
typename std::enable_if<std::is_integral<T>::value &&
|
||||||
|
std::is_integral<U>::value>::type> {
|
||||||
|
using result_type = typename MaxExponentPromotion<T, U>::type;
|
||||||
|
template <typename V>
|
||||||
|
static constexpr bool Do(T x, U y, V* result) {
|
||||||
|
// TODO(jschuh) Make this "constexpr if" once we're C++17.
|
||||||
|
if (CheckedMulFastOp<T, U>::is_supported)
|
||||||
|
return CheckedMulFastOp<T, U>::Do(x, y, result);
|
||||||
|
|
||||||
|
using Promotion = typename FastIntegerArithmeticPromotion<T, U>::type;
|
||||||
|
// Verify the destination type can hold the result (always true for 0).
|
||||||
|
if (BASE_NUMERICS_UNLIKELY((!IsValueInRangeForNumericType<Promotion>(x) ||
|
||||||
|
!IsValueInRangeForNumericType<Promotion>(y)) &&
|
||||||
|
x && y)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Promotion presult = {};
|
||||||
|
bool is_valid = true;
|
||||||
|
if (CheckedMulFastOp<Promotion, Promotion>::is_supported) {
|
||||||
|
// The fast op may be available with the promoted type.
|
||||||
|
is_valid = CheckedMulFastOp<Promotion, Promotion>::Do(x, y, &presult);
|
||||||
|
} else if (IsIntegerArithmeticSafe<Promotion, T, U>::value) {
|
||||||
|
presult = static_cast<Promotion>(x) * static_cast<Promotion>(y);
|
||||||
|
} else {
|
||||||
|
is_valid = CheckedMulImpl(static_cast<Promotion>(x),
|
||||||
|
static_cast<Promotion>(y), &presult);
|
||||||
|
}
|
||||||
|
*result = static_cast<V>(presult);
|
||||||
|
return is_valid && IsValueInRangeForNumericType<V>(presult);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Division just requires a check for a zero denominator or an invalid negation
|
||||||
|
// on signed min/-1.
|
||||||
|
template <typename T, typename U, class Enable = void>
|
||||||
|
struct CheckedDivOp {};
|
||||||
|
|
||||||
|
template <typename T, typename U>
|
||||||
|
struct CheckedDivOp<T,
|
||||||
|
U,
|
||||||
|
typename std::enable_if<std::is_integral<T>::value &&
|
||||||
|
std::is_integral<U>::value>::type> {
|
||||||
|
using result_type = typename MaxExponentPromotion<T, U>::type;
|
||||||
|
template <typename V>
|
||||||
|
static constexpr bool Do(T x, U y, V* result) {
|
||||||
|
if (BASE_NUMERICS_UNLIKELY(!y))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// The overflow check can be compiled away if we don't have the exact
|
||||||
|
// combination of types needed to trigger this case.
|
||||||
|
using Promotion = typename BigEnoughPromotion<T, U>::type;
|
||||||
|
if (BASE_NUMERICS_UNLIKELY(
|
||||||
|
(std::is_signed<T>::value && std::is_signed<U>::value &&
|
||||||
|
IsTypeInRangeForNumericType<T, Promotion>::value &&
|
||||||
|
static_cast<Promotion>(x) ==
|
||||||
|
std::numeric_limits<Promotion>::lowest() &&
|
||||||
|
y == static_cast<U>(-1)))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This branch always compiles away if the above branch wasn't removed.
|
||||||
|
if (BASE_NUMERICS_UNLIKELY((!IsValueInRangeForNumericType<Promotion>(x) ||
|
||||||
|
!IsValueInRangeForNumericType<Promotion>(y)) &&
|
||||||
|
x)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Promotion presult = Promotion(x) / Promotion(y);
|
||||||
|
*result = static_cast<V>(presult);
|
||||||
|
return IsValueInRangeForNumericType<V>(presult);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename U, class Enable = void>
|
||||||
|
struct CheckedModOp {};
|
||||||
|
|
||||||
|
template <typename T, typename U>
|
||||||
|
struct CheckedModOp<T,
|
||||||
|
U,
|
||||||
|
typename std::enable_if<std::is_integral<T>::value &&
|
||||||
|
std::is_integral<U>::value>::type> {
|
||||||
|
using result_type = typename MaxExponentPromotion<T, U>::type;
|
||||||
|
template <typename V>
|
||||||
|
static constexpr bool Do(T x, U y, V* result) {
|
||||||
|
using Promotion = typename BigEnoughPromotion<T, U>::type;
|
||||||
|
if (BASE_NUMERICS_LIKELY(y)) {
|
||||||
|
Promotion presult = static_cast<Promotion>(x) % static_cast<Promotion>(y);
|
||||||
|
*result = static_cast<Promotion>(presult);
|
||||||
|
return IsValueInRangeForNumericType<V>(presult);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename U, class Enable = void>
|
||||||
|
struct CheckedLshOp {};
|
||||||
|
|
||||||
|
// Left shift. Shifts less than 0 or greater than or equal to the number
|
||||||
|
// of bits in the promoted type are undefined. Shifts of negative values
|
||||||
|
// are undefined. Otherwise it is defined when the result fits.
|
||||||
|
template <typename T, typename U>
|
||||||
|
struct CheckedLshOp<T,
|
||||||
|
U,
|
||||||
|
typename std::enable_if<std::is_integral<T>::value &&
|
||||||
|
std::is_integral<U>::value>::type> {
|
||||||
|
using result_type = T;
|
||||||
|
template <typename V>
|
||||||
|
static constexpr bool Do(T x, U shift, V* result) {
|
||||||
|
// Disallow negative numbers and verify the shift is in bounds.
|
||||||
|
if (BASE_NUMERICS_LIKELY(!IsValueNegative(x) &&
|
||||||
|
as_unsigned(shift) <
|
||||||
|
as_unsigned(std::numeric_limits<T>::digits))) {
|
||||||
|
// Shift as unsigned to avoid undefined behavior.
|
||||||
|
*result = static_cast<V>(as_unsigned(x) << shift);
|
||||||
|
// If the shift can be reversed, we know it was valid.
|
||||||
|
return *result >> shift == x;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle the legal corner-case of a full-width signed shift of zero.
|
||||||
|
return std::is_signed<T>::value && !x &&
|
||||||
|
as_unsigned(shift) == as_unsigned(std::numeric_limits<T>::digits);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename U, class Enable = void>
|
||||||
|
struct CheckedRshOp {};
|
||||||
|
|
||||||
|
// Right shift. Shifts less than 0 or greater than or equal to the number
|
||||||
|
// of bits in the promoted type are undefined. Otherwise, it is always defined,
|
||||||
|
// but a right shift of a negative value is implementation-dependent.
|
||||||
|
template <typename T, typename U>
|
||||||
|
struct CheckedRshOp<T,
|
||||||
|
U,
|
||||||
|
typename std::enable_if<std::is_integral<T>::value &&
|
||||||
|
std::is_integral<U>::value>::type> {
|
||||||
|
using result_type = T;
|
||||||
|
template <typename V>
|
||||||
|
static bool Do(T x, U shift, V* result) {
|
||||||
|
// Use the type conversion push negative values out of range.
|
||||||
|
if (BASE_NUMERICS_LIKELY(as_unsigned(shift) <
|
||||||
|
IntegerBitsPlusSign<T>::value)) {
|
||||||
|
T tmp = x >> shift;
|
||||||
|
*result = static_cast<V>(tmp);
|
||||||
|
return IsValueInRangeForNumericType<V>(tmp);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename U, class Enable = void>
|
||||||
|
struct CheckedAndOp {};
|
||||||
|
|
||||||
|
// For simplicity we support only unsigned integer results.
|
||||||
|
template <typename T, typename U>
|
||||||
|
struct CheckedAndOp<T,
|
||||||
|
U,
|
||||||
|
typename std::enable_if<std::is_integral<T>::value &&
|
||||||
|
std::is_integral<U>::value>::type> {
|
||||||
|
using result_type = typename std::make_unsigned<
|
||||||
|
typename MaxExponentPromotion<T, U>::type>::type;
|
||||||
|
template <typename V>
|
||||||
|
static constexpr bool Do(T x, U y, V* result) {
|
||||||
|
result_type tmp = static_cast<result_type>(x) & static_cast<result_type>(y);
|
||||||
|
*result = static_cast<V>(tmp);
|
||||||
|
return IsValueInRangeForNumericType<V>(tmp);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename U, class Enable = void>
|
||||||
|
struct CheckedOrOp {};
|
||||||
|
|
||||||
|
// For simplicity we support only unsigned integers.
|
||||||
|
template <typename T, typename U>
|
||||||
|
struct CheckedOrOp<T,
|
||||||
|
U,
|
||||||
|
typename std::enable_if<std::is_integral<T>::value &&
|
||||||
|
std::is_integral<U>::value>::type> {
|
||||||
|
using result_type = typename std::make_unsigned<
|
||||||
|
typename MaxExponentPromotion<T, U>::type>::type;
|
||||||
|
template <typename V>
|
||||||
|
static constexpr bool Do(T x, U y, V* result) {
|
||||||
|
result_type tmp = static_cast<result_type>(x) | static_cast<result_type>(y);
|
||||||
|
*result = static_cast<V>(tmp);
|
||||||
|
return IsValueInRangeForNumericType<V>(tmp);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename U, class Enable = void>
|
||||||
|
struct CheckedXorOp {};
|
||||||
|
|
||||||
|
// For simplicity we support only unsigned integers.
|
||||||
|
template <typename T, typename U>
|
||||||
|
struct CheckedXorOp<T,
|
||||||
|
U,
|
||||||
|
typename std::enable_if<std::is_integral<T>::value &&
|
||||||
|
std::is_integral<U>::value>::type> {
|
||||||
|
using result_type = typename std::make_unsigned<
|
||||||
|
typename MaxExponentPromotion<T, U>::type>::type;
|
||||||
|
template <typename V>
|
||||||
|
static constexpr bool Do(T x, U y, V* result) {
|
||||||
|
result_type tmp = static_cast<result_type>(x) ^ static_cast<result_type>(y);
|
||||||
|
*result = static_cast<V>(tmp);
|
||||||
|
return IsValueInRangeForNumericType<V>(tmp);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Max doesn't really need to be implemented this way because it can't fail,
|
||||||
|
// but it makes the code much cleaner to use the MathOp wrappers.
|
||||||
|
template <typename T, typename U, class Enable = void>
|
||||||
|
struct CheckedMaxOp {};
|
||||||
|
|
||||||
|
template <typename T, typename U>
|
||||||
|
struct CheckedMaxOp<
|
||||||
|
T,
|
||||||
|
U,
|
||||||
|
typename std::enable_if<std::is_arithmetic<T>::value &&
|
||||||
|
std::is_arithmetic<U>::value>::type> {
|
||||||
|
using result_type = typename MaxExponentPromotion<T, U>::type;
|
||||||
|
template <typename V>
|
||||||
|
static constexpr bool Do(T x, U y, V* result) {
|
||||||
|
result_type tmp = IsGreater<T, U>::Test(x, y) ? static_cast<result_type>(x)
|
||||||
|
: static_cast<result_type>(y);
|
||||||
|
*result = static_cast<V>(tmp);
|
||||||
|
return IsValueInRangeForNumericType<V>(tmp);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Min doesn't really need to be implemented this way because it can't fail,
|
||||||
|
// but it makes the code much cleaner to use the MathOp wrappers.
|
||||||
|
template <typename T, typename U, class Enable = void>
|
||||||
|
struct CheckedMinOp {};
|
||||||
|
|
||||||
|
template <typename T, typename U>
|
||||||
|
struct CheckedMinOp<
|
||||||
|
T,
|
||||||
|
U,
|
||||||
|
typename std::enable_if<std::is_arithmetic<T>::value &&
|
||||||
|
std::is_arithmetic<U>::value>::type> {
|
||||||
|
using result_type = typename LowestValuePromotion<T, U>::type;
|
||||||
|
template <typename V>
|
||||||
|
static constexpr bool Do(T x, U y, V* result) {
|
||||||
|
result_type tmp = IsLess<T, U>::Test(x, y) ? static_cast<result_type>(x)
|
||||||
|
: static_cast<result_type>(y);
|
||||||
|
*result = static_cast<V>(tmp);
|
||||||
|
return IsValueInRangeForNumericType<V>(tmp);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// This is just boilerplate that wraps the standard floating point arithmetic.
|
||||||
|
// A macro isn't the nicest solution, but it beats rewriting these repeatedly.
|
||||||
|
#define BASE_FLOAT_ARITHMETIC_OPS(NAME, OP) \
|
||||||
|
template <typename T, typename U> \
|
||||||
|
struct Checked##NAME##Op< \
|
||||||
|
T, U, \
|
||||||
|
typename std::enable_if<std::is_floating_point<T>::value || \
|
||||||
|
std::is_floating_point<U>::value>::type> { \
|
||||||
|
using result_type = typename MaxExponentPromotion<T, U>::type; \
|
||||||
|
template <typename V> \
|
||||||
|
static constexpr bool Do(T x, U y, V* result) { \
|
||||||
|
using Promotion = typename MaxExponentPromotion<T, U>::type; \
|
||||||
|
Promotion presult = x OP y; \
|
||||||
|
*result = static_cast<V>(presult); \
|
||||||
|
return IsValueInRangeForNumericType<V>(presult); \
|
||||||
|
} \
|
||||||
|
};
|
||||||
|
|
||||||
|
BASE_FLOAT_ARITHMETIC_OPS(Add, +)
|
||||||
|
BASE_FLOAT_ARITHMETIC_OPS(Sub, -)
|
||||||
|
BASE_FLOAT_ARITHMETIC_OPS(Mul, *)
|
||||||
|
BASE_FLOAT_ARITHMETIC_OPS(Div, /)
|
||||||
|
|
||||||
|
#undef BASE_FLOAT_ARITHMETIC_OPS
|
||||||
|
|
||||||
|
// Floats carry around their validity state with them, but integers do not. So,
|
||||||
|
// we wrap the underlying value in a specialization in order to hide that detail
|
||||||
|
// and expose an interface via accessors.
|
||||||
|
enum NumericRepresentation {
|
||||||
|
NUMERIC_INTEGER,
|
||||||
|
NUMERIC_FLOATING,
|
||||||
|
NUMERIC_UNKNOWN
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename NumericType>
|
||||||
|
struct GetNumericRepresentation {
|
||||||
|
static const NumericRepresentation value =
|
||||||
|
std::is_integral<NumericType>::value
|
||||||
|
? NUMERIC_INTEGER
|
||||||
|
: (std::is_floating_point<NumericType>::value ? NUMERIC_FLOATING
|
||||||
|
: NUMERIC_UNKNOWN);
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T,
|
||||||
|
NumericRepresentation type = GetNumericRepresentation<T>::value>
|
||||||
|
class CheckedNumericState {};
|
||||||
|
|
||||||
|
// Integrals require quite a bit of additional housekeeping to manage state.
|
||||||
|
template <typename T>
|
||||||
|
class CheckedNumericState<T, NUMERIC_INTEGER> {
|
||||||
|
private:
|
||||||
|
// is_valid_ precedes value_ because member intializers in the constructors
|
||||||
|
// are evaluated in field order, and is_valid_ must be read when initializing
|
||||||
|
// value_.
|
||||||
|
bool is_valid_;
|
||||||
|
T value_;
|
||||||
|
|
||||||
|
// Ensures that a type conversion does not trigger undefined behavior.
|
||||||
|
template <typename Src>
|
||||||
|
static constexpr T WellDefinedConversionOrZero(const Src value,
|
||||||
|
const bool is_valid) {
|
||||||
|
using SrcType = typename internal::UnderlyingType<Src>::type;
|
||||||
|
return (std::is_integral<SrcType>::value || is_valid)
|
||||||
|
? static_cast<T>(value)
|
||||||
|
: static_cast<T>(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
template <typename Src, NumericRepresentation type>
|
||||||
|
friend class CheckedNumericState;
|
||||||
|
|
||||||
|
constexpr CheckedNumericState() : is_valid_(true), value_(0) {}
|
||||||
|
|
||||||
|
template <typename Src>
|
||||||
|
constexpr CheckedNumericState(Src value, bool is_valid)
|
||||||
|
: is_valid_(is_valid && IsValueInRangeForNumericType<T>(value)),
|
||||||
|
value_(WellDefinedConversionOrZero(value, is_valid_)) {
|
||||||
|
static_assert(std::is_arithmetic<Src>::value, "Argument must be numeric.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy constructor.
|
||||||
|
template <typename Src>
|
||||||
|
constexpr CheckedNumericState(const CheckedNumericState<Src>& rhs)
|
||||||
|
: is_valid_(rhs.IsValid()),
|
||||||
|
value_(WellDefinedConversionOrZero(rhs.value(), is_valid_)) {}
|
||||||
|
|
||||||
|
template <typename Src>
|
||||||
|
constexpr explicit CheckedNumericState(Src value)
|
||||||
|
: is_valid_(IsValueInRangeForNumericType<T>(value)),
|
||||||
|
value_(WellDefinedConversionOrZero(value, is_valid_)) {}
|
||||||
|
|
||||||
|
constexpr bool is_valid() const { return is_valid_; }
|
||||||
|
constexpr T value() const { return value_; }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Floating points maintain their own validity, but need translation wrappers.
|
||||||
|
template <typename T>
|
||||||
|
class CheckedNumericState<T, NUMERIC_FLOATING> {
|
||||||
|
private:
|
||||||
|
T value_;
|
||||||
|
|
||||||
|
// Ensures that a type conversion does not trigger undefined behavior.
|
||||||
|
template <typename Src>
|
||||||
|
static constexpr T WellDefinedConversionOrNaN(const Src value,
|
||||||
|
const bool is_valid) {
|
||||||
|
using SrcType = typename internal::UnderlyingType<Src>::type;
|
||||||
|
return (StaticDstRangeRelationToSrcRange<T, SrcType>::value ==
|
||||||
|
NUMERIC_RANGE_CONTAINED ||
|
||||||
|
is_valid)
|
||||||
|
? static_cast<T>(value)
|
||||||
|
: std::numeric_limits<T>::quiet_NaN();
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
template <typename Src, NumericRepresentation type>
|
||||||
|
friend class CheckedNumericState;
|
||||||
|
|
||||||
|
constexpr CheckedNumericState() : value_(0.0) {}
|
||||||
|
|
||||||
|
template <typename Src>
|
||||||
|
constexpr CheckedNumericState(Src value, bool is_valid)
|
||||||
|
: value_(WellDefinedConversionOrNaN(value, is_valid)) {}
|
||||||
|
|
||||||
|
template <typename Src>
|
||||||
|
constexpr explicit CheckedNumericState(Src value)
|
||||||
|
: value_(WellDefinedConversionOrNaN(
|
||||||
|
value,
|
||||||
|
IsValueInRangeForNumericType<T>(value))) {}
|
||||||
|
|
||||||
|
// Copy constructor.
|
||||||
|
template <typename Src>
|
||||||
|
constexpr CheckedNumericState(const CheckedNumericState<Src>& rhs)
|
||||||
|
: value_(WellDefinedConversionOrNaN(
|
||||||
|
rhs.value(),
|
||||||
|
rhs.is_valid() && IsValueInRangeForNumericType<T>(rhs.value()))) {}
|
||||||
|
|
||||||
|
constexpr bool is_valid() const {
|
||||||
|
// Written this way because std::isfinite is not reliably constexpr.
|
||||||
|
return MustTreatAsConstexpr(value_)
|
||||||
|
? value_ <= std::numeric_limits<T>::max() &&
|
||||||
|
value_ >= std::numeric_limits<T>::lowest()
|
||||||
|
: std::isfinite(value_);
|
||||||
|
}
|
||||||
|
constexpr T value() const { return value_; }
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace base
|
||||||
|
|
||||||
|
#endif // BASE_NUMERICS_CHECKED_MATH_IMPL_H_
|
|
@ -0,0 +1,264 @@
|
||||||
|
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#ifndef BASE_NUMERICS_CLAMPED_MATH_H_
|
||||||
|
#define BASE_NUMERICS_CLAMPED_MATH_H_
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
#include "base/numerics/clamped_math_impl.h"
|
||||||
|
|
||||||
|
namespace base {
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class ClampedNumeric {
|
||||||
|
static_assert(std::is_arithmetic<T>::value,
|
||||||
|
"ClampedNumeric<T>: T must be a numeric type.");
|
||||||
|
|
||||||
|
public:
|
||||||
|
using type = T;
|
||||||
|
|
||||||
|
constexpr ClampedNumeric() : value_(0) {}
|
||||||
|
|
||||||
|
// Copy constructor.
|
||||||
|
template <typename Src>
|
||||||
|
constexpr ClampedNumeric(const ClampedNumeric<Src>& rhs)
|
||||||
|
: value_(saturated_cast<T>(rhs.value_)) {}
|
||||||
|
|
||||||
|
template <typename Src>
|
||||||
|
friend class ClampedNumeric;
|
||||||
|
|
||||||
|
// This is not an explicit constructor because we implicitly upgrade regular
|
||||||
|
// numerics to ClampedNumerics to make them easier to use.
|
||||||
|
template <typename Src>
|
||||||
|
constexpr ClampedNumeric(Src value) // NOLINT(runtime/explicit)
|
||||||
|
: value_(saturated_cast<T>(value)) {
|
||||||
|
static_assert(std::is_arithmetic<Src>::value, "Argument must be numeric.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is not an explicit constructor because we want a seamless conversion
|
||||||
|
// from StrictNumeric types.
|
||||||
|
template <typename Src>
|
||||||
|
constexpr ClampedNumeric(
|
||||||
|
StrictNumeric<Src> value) // NOLINT(runtime/explicit)
|
||||||
|
: value_(saturated_cast<T>(static_cast<Src>(value))) {}
|
||||||
|
|
||||||
|
// Returns a ClampedNumeric of the specified type, cast from the current
|
||||||
|
// ClampedNumeric, and saturated to the destination type.
|
||||||
|
template <typename Dst>
|
||||||
|
constexpr ClampedNumeric<typename UnderlyingType<Dst>::type> Cast() const {
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prototypes for the supported arithmetic operator overloads.
|
||||||
|
template <typename Src>
|
||||||
|
constexpr ClampedNumeric& operator+=(const Src rhs);
|
||||||
|
template <typename Src>
|
||||||
|
constexpr ClampedNumeric& operator-=(const Src rhs);
|
||||||
|
template <typename Src>
|
||||||
|
constexpr ClampedNumeric& operator*=(const Src rhs);
|
||||||
|
template <typename Src>
|
||||||
|
constexpr ClampedNumeric& operator/=(const Src rhs);
|
||||||
|
template <typename Src>
|
||||||
|
constexpr ClampedNumeric& operator%=(const Src rhs);
|
||||||
|
template <typename Src>
|
||||||
|
constexpr ClampedNumeric& operator<<=(const Src rhs);
|
||||||
|
template <typename Src>
|
||||||
|
constexpr ClampedNumeric& operator>>=(const Src rhs);
|
||||||
|
template <typename Src>
|
||||||
|
constexpr ClampedNumeric& operator&=(const Src rhs);
|
||||||
|
template <typename Src>
|
||||||
|
constexpr ClampedNumeric& operator|=(const Src rhs);
|
||||||
|
template <typename Src>
|
||||||
|
constexpr ClampedNumeric& operator^=(const Src rhs);
|
||||||
|
|
||||||
|
constexpr ClampedNumeric operator-() const {
|
||||||
|
// The negation of two's complement int min is int min, so that's the
|
||||||
|
// only overflow case where we will saturate.
|
||||||
|
return ClampedNumeric<T>(SaturatedNegWrapper(value_));
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr ClampedNumeric operator~() const {
|
||||||
|
return ClampedNumeric<decltype(InvertWrapper(T()))>(InvertWrapper(value_));
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr ClampedNumeric Abs() const {
|
||||||
|
// The negation of two's complement int min is int min, so that's the
|
||||||
|
// only overflow case where we will saturate.
|
||||||
|
return ClampedNumeric<T>(SaturatedAbsWrapper(value_));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename U>
|
||||||
|
constexpr ClampedNumeric<typename MathWrapper<ClampedMaxOp, T, U>::type> Max(
|
||||||
|
const U rhs) const {
|
||||||
|
using result_type = typename MathWrapper<ClampedMaxOp, T, U>::type;
|
||||||
|
return ClampedNumeric<result_type>(
|
||||||
|
ClampedMaxOp<T, U>::Do(value_, Wrapper<U>::value(rhs)));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename U>
|
||||||
|
constexpr ClampedNumeric<typename MathWrapper<ClampedMinOp, T, U>::type> Min(
|
||||||
|
const U rhs) const {
|
||||||
|
using result_type = typename MathWrapper<ClampedMinOp, T, U>::type;
|
||||||
|
return ClampedNumeric<result_type>(
|
||||||
|
ClampedMinOp<T, U>::Do(value_, Wrapper<U>::value(rhs)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function is available only for integral types. It returns an unsigned
|
||||||
|
// integer of the same width as the source type, containing the absolute value
|
||||||
|
// of the source, and properly handling signed min.
|
||||||
|
constexpr ClampedNumeric<typename UnsignedOrFloatForSize<T>::type>
|
||||||
|
UnsignedAbs() const {
|
||||||
|
return ClampedNumeric<typename UnsignedOrFloatForSize<T>::type>(
|
||||||
|
SafeUnsignedAbs(value_));
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr ClampedNumeric& operator++() {
|
||||||
|
*this += 1;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr ClampedNumeric operator++(int) {
|
||||||
|
ClampedNumeric value = *this;
|
||||||
|
*this += 1;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr ClampedNumeric& operator--() {
|
||||||
|
*this -= 1;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr ClampedNumeric operator--(int) {
|
||||||
|
ClampedNumeric value = *this;
|
||||||
|
*this -= 1;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// These perform the actual math operations on the ClampedNumerics.
|
||||||
|
// Binary arithmetic operations.
|
||||||
|
template <template <typename, typename, typename> class M,
|
||||||
|
typename L,
|
||||||
|
typename R>
|
||||||
|
static constexpr ClampedNumeric MathOp(const L lhs, const R rhs) {
|
||||||
|
using Math = typename MathWrapper<M, L, R>::math;
|
||||||
|
return ClampedNumeric<T>(
|
||||||
|
Math::template Do<T>(Wrapper<L>::value(lhs), Wrapper<R>::value(rhs)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assignment arithmetic operations.
|
||||||
|
template <template <typename, typename, typename> class M, typename R>
|
||||||
|
constexpr ClampedNumeric& MathOp(const R rhs) {
|
||||||
|
using Math = typename MathWrapper<M, T, R>::math;
|
||||||
|
*this =
|
||||||
|
ClampedNumeric<T>(Math::template Do<T>(value_, Wrapper<R>::value(rhs)));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Dst>
|
||||||
|
constexpr operator Dst() const {
|
||||||
|
return saturated_cast<typename ArithmeticOrUnderlyingEnum<Dst>::type>(
|
||||||
|
value_);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This method extracts the raw integer value without saturating it to the
|
||||||
|
// destination type as the conversion operator does. This is useful when
|
||||||
|
// e.g. assigning to an auto type or passing as a deduced template parameter.
|
||||||
|
constexpr T RawValue() const { return value_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
T value_;
|
||||||
|
|
||||||
|
// These wrappers allow us to handle state the same way for both
|
||||||
|
// ClampedNumeric and POD arithmetic types.
|
||||||
|
template <typename Src>
|
||||||
|
struct Wrapper {
|
||||||
|
static constexpr Src value(Src value) {
|
||||||
|
return static_cast<typename UnderlyingType<Src>::type>(value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Convience wrapper to return a new ClampedNumeric from the provided arithmetic
|
||||||
|
// or ClampedNumericType.
|
||||||
|
template <typename T>
|
||||||
|
constexpr ClampedNumeric<typename UnderlyingType<T>::type> MakeClampedNum(
|
||||||
|
const T value) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !BASE_NUMERICS_DISABLE_OSTREAM_OPERATORS
|
||||||
|
// Overload the ostream output operator to make logging work nicely.
|
||||||
|
template <typename T>
|
||||||
|
std::ostream& operator<<(std::ostream& os, const ClampedNumeric<T>& value) {
|
||||||
|
os << static_cast<T>(value);
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// These implement the variadic wrapper for the math operations.
|
||||||
|
template <template <typename, typename, typename> class M,
|
||||||
|
typename L,
|
||||||
|
typename R>
|
||||||
|
constexpr ClampedNumeric<typename MathWrapper<M, L, R>::type> ClampMathOp(
|
||||||
|
const L lhs,
|
||||||
|
const R rhs) {
|
||||||
|
using Math = typename MathWrapper<M, L, R>::math;
|
||||||
|
return ClampedNumeric<typename Math::result_type>::template MathOp<M>(lhs,
|
||||||
|
rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
// General purpose wrapper template for arithmetic operations.
|
||||||
|
template <template <typename, typename, typename> class M,
|
||||||
|
typename L,
|
||||||
|
typename R,
|
||||||
|
typename... Args>
|
||||||
|
constexpr ClampedNumeric<typename ResultType<M, L, R, Args...>::type>
|
||||||
|
ClampMathOp(const L lhs, const R rhs, const Args... args) {
|
||||||
|
return ClampMathOp<M>(ClampMathOp<M>(lhs, rhs), args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Add, +, +=)
|
||||||
|
BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Sub, -, -=)
|
||||||
|
BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Mul, *, *=)
|
||||||
|
BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Div, /, /=)
|
||||||
|
BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Mod, %, %=)
|
||||||
|
BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Lsh, <<, <<=)
|
||||||
|
BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Rsh, >>, >>=)
|
||||||
|
BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, And, &, &=)
|
||||||
|
BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Or, |, |=)
|
||||||
|
BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Xor, ^, ^=)
|
||||||
|
BASE_NUMERIC_ARITHMETIC_VARIADIC(Clamped, Clamp, Max)
|
||||||
|
BASE_NUMERIC_ARITHMETIC_VARIADIC(Clamped, Clamp, Min)
|
||||||
|
BASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsLess, <)
|
||||||
|
BASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsLessOrEqual, <=)
|
||||||
|
BASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsGreater, >)
|
||||||
|
BASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsGreaterOrEqual, >=)
|
||||||
|
BASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsEqual, ==)
|
||||||
|
BASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsNotEqual, !=)
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
using internal::ClampedNumeric;
|
||||||
|
using internal::MakeClampedNum;
|
||||||
|
using internal::ClampMax;
|
||||||
|
using internal::ClampMin;
|
||||||
|
using internal::ClampAdd;
|
||||||
|
using internal::ClampSub;
|
||||||
|
using internal::ClampMul;
|
||||||
|
using internal::ClampDiv;
|
||||||
|
using internal::ClampMod;
|
||||||
|
using internal::ClampLsh;
|
||||||
|
using internal::ClampRsh;
|
||||||
|
using internal::ClampAnd;
|
||||||
|
using internal::ClampOr;
|
||||||
|
using internal::ClampXor;
|
||||||
|
|
||||||
|
} // namespace base
|
||||||
|
|
||||||
|
#endif // BASE_NUMERICS_CLAMPED_MATH_H_
|
|
@ -0,0 +1,341 @@
|
||||||
|
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#ifndef BASE_NUMERICS_CLAMPED_MATH_IMPL_H_
|
||||||
|
#define BASE_NUMERICS_CLAMPED_MATH_IMPL_H_
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <climits>
|
||||||
|
#include <cmath>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <limits>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
#include "base/numerics/checked_math.h"
|
||||||
|
#include "base/numerics/safe_conversions.h"
|
||||||
|
#include "base/numerics/safe_math_shared_impl.h"
|
||||||
|
|
||||||
|
namespace base {
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
template <typename T,
|
||||||
|
typename std::enable_if<std::is_integral<T>::value &&
|
||||||
|
std::is_signed<T>::value>::type* = nullptr>
|
||||||
|
constexpr T SaturatedNegWrapper(T value) {
|
||||||
|
return MustTreatAsConstexpr(value) || !ClampedNegFastOp<T>::is_supported
|
||||||
|
? (NegateWrapper(value) != std::numeric_limits<T>::lowest()
|
||||||
|
? NegateWrapper(value)
|
||||||
|
: std::numeric_limits<T>::max())
|
||||||
|
: ClampedNegFastOp<T>::Do(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T,
|
||||||
|
typename std::enable_if<std::is_integral<T>::value &&
|
||||||
|
!std::is_signed<T>::value>::type* = nullptr>
|
||||||
|
constexpr T SaturatedNegWrapper(T value) {
|
||||||
|
return T(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <
|
||||||
|
typename T,
|
||||||
|
typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
|
||||||
|
constexpr T SaturatedNegWrapper(T value) {
|
||||||
|
return -value;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T,
|
||||||
|
typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
|
||||||
|
constexpr T SaturatedAbsWrapper(T value) {
|
||||||
|
// The calculation below is a static identity for unsigned types, but for
|
||||||
|
// signed integer types it provides a non-branching, saturated absolute value.
|
||||||
|
// This works because SafeUnsignedAbs() returns an unsigned type, which can
|
||||||
|
// represent the absolute value of all negative numbers of an equal-width
|
||||||
|
// integer type. The call to IsValueNegative() then detects overflow in the
|
||||||
|
// special case of numeric_limits<T>::min(), by evaluating the bit pattern as
|
||||||
|
// a signed integer value. If it is the overflow case, we end up subtracting
|
||||||
|
// one from the unsigned result, thus saturating to numeric_limits<T>::max().
|
||||||
|
return static_cast<T>(SafeUnsignedAbs(value) -
|
||||||
|
IsValueNegative<T>(SafeUnsignedAbs(value)));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <
|
||||||
|
typename T,
|
||||||
|
typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
|
||||||
|
constexpr T SaturatedAbsWrapper(T value) {
|
||||||
|
return value < 0 ? -value : value;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename U, class Enable = void>
|
||||||
|
struct ClampedAddOp {};
|
||||||
|
|
||||||
|
template <typename T, typename U>
|
||||||
|
struct ClampedAddOp<T,
|
||||||
|
U,
|
||||||
|
typename std::enable_if<std::is_integral<T>::value &&
|
||||||
|
std::is_integral<U>::value>::type> {
|
||||||
|
using result_type = typename MaxExponentPromotion<T, U>::type;
|
||||||
|
template <typename V = result_type>
|
||||||
|
static constexpr V Do(T x, U y) {
|
||||||
|
if (ClampedAddFastOp<T, U>::is_supported)
|
||||||
|
return ClampedAddFastOp<T, U>::template Do<V>(x, y);
|
||||||
|
|
||||||
|
static_assert(std::is_same<V, result_type>::value ||
|
||||||
|
IsTypeInRangeForNumericType<U, V>::value,
|
||||||
|
"The saturation result cannot be determined from the "
|
||||||
|
"provided types.");
|
||||||
|
const V saturated = CommonMaxOrMin<V>(IsValueNegative(y));
|
||||||
|
V result = {};
|
||||||
|
return BASE_NUMERICS_LIKELY((CheckedAddOp<T, U>::Do(x, y, &result)))
|
||||||
|
? result
|
||||||
|
: saturated;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename U, class Enable = void>
|
||||||
|
struct ClampedSubOp {};
|
||||||
|
|
||||||
|
template <typename T, typename U>
|
||||||
|
struct ClampedSubOp<T,
|
||||||
|
U,
|
||||||
|
typename std::enable_if<std::is_integral<T>::value &&
|
||||||
|
std::is_integral<U>::value>::type> {
|
||||||
|
using result_type = typename MaxExponentPromotion<T, U>::type;
|
||||||
|
template <typename V = result_type>
|
||||||
|
static constexpr V Do(T x, U y) {
|
||||||
|
// TODO(jschuh) Make this "constexpr if" once we're C++17.
|
||||||
|
if (ClampedSubFastOp<T, U>::is_supported)
|
||||||
|
return ClampedSubFastOp<T, U>::template Do<V>(x, y);
|
||||||
|
|
||||||
|
static_assert(std::is_same<V, result_type>::value ||
|
||||||
|
IsTypeInRangeForNumericType<U, V>::value,
|
||||||
|
"The saturation result cannot be determined from the "
|
||||||
|
"provided types.");
|
||||||
|
const V saturated = CommonMaxOrMin<V>(!IsValueNegative(y));
|
||||||
|
V result = {};
|
||||||
|
return BASE_NUMERICS_LIKELY((CheckedSubOp<T, U>::Do(x, y, &result)))
|
||||||
|
? result
|
||||||
|
: saturated;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename U, class Enable = void>
|
||||||
|
struct ClampedMulOp {};
|
||||||
|
|
||||||
|
template <typename T, typename U>
|
||||||
|
struct ClampedMulOp<T,
|
||||||
|
U,
|
||||||
|
typename std::enable_if<std::is_integral<T>::value &&
|
||||||
|
std::is_integral<U>::value>::type> {
|
||||||
|
using result_type = typename MaxExponentPromotion<T, U>::type;
|
||||||
|
template <typename V = result_type>
|
||||||
|
static constexpr V Do(T x, U y) {
|
||||||
|
// TODO(jschuh) Make this "constexpr if" once we're C++17.
|
||||||
|
if (ClampedMulFastOp<T, U>::is_supported)
|
||||||
|
return ClampedMulFastOp<T, U>::template Do<V>(x, y);
|
||||||
|
|
||||||
|
V result = {};
|
||||||
|
const V saturated =
|
||||||
|
CommonMaxOrMin<V>(IsValueNegative(x) ^ IsValueNegative(y));
|
||||||
|
return BASE_NUMERICS_LIKELY((CheckedMulOp<T, U>::Do(x, y, &result)))
|
||||||
|
? result
|
||||||
|
: saturated;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename U, class Enable = void>
|
||||||
|
struct ClampedDivOp {};
|
||||||
|
|
||||||
|
template <typename T, typename U>
|
||||||
|
struct ClampedDivOp<T,
|
||||||
|
U,
|
||||||
|
typename std::enable_if<std::is_integral<T>::value &&
|
||||||
|
std::is_integral<U>::value>::type> {
|
||||||
|
using result_type = typename MaxExponentPromotion<T, U>::type;
|
||||||
|
template <typename V = result_type>
|
||||||
|
static constexpr V Do(T x, U y) {
|
||||||
|
V result = {};
|
||||||
|
if (BASE_NUMERICS_LIKELY((CheckedDivOp<T, U>::Do(x, y, &result))))
|
||||||
|
return result;
|
||||||
|
// Saturation goes to max, min, or NaN (if x is zero).
|
||||||
|
return x ? CommonMaxOrMin<V>(IsValueNegative(x) ^ IsValueNegative(y))
|
||||||
|
: SaturationDefaultLimits<V>::NaN();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename U, class Enable = void>
|
||||||
|
struct ClampedModOp {};
|
||||||
|
|
||||||
|
template <typename T, typename U>
|
||||||
|
struct ClampedModOp<T,
|
||||||
|
U,
|
||||||
|
typename std::enable_if<std::is_integral<T>::value &&
|
||||||
|
std::is_integral<U>::value>::type> {
|
||||||
|
using result_type = typename MaxExponentPromotion<T, U>::type;
|
||||||
|
template <typename V = result_type>
|
||||||
|
static constexpr V Do(T x, U y) {
|
||||||
|
V result = {};
|
||||||
|
return BASE_NUMERICS_LIKELY((CheckedModOp<T, U>::Do(x, y, &result)))
|
||||||
|
? result
|
||||||
|
: x;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename U, class Enable = void>
|
||||||
|
struct ClampedLshOp {};
|
||||||
|
|
||||||
|
// Left shift. Non-zero values saturate in the direction of the sign. A zero
|
||||||
|
// shifted by any value always results in zero.
|
||||||
|
template <typename T, typename U>
|
||||||
|
struct ClampedLshOp<T,
|
||||||
|
U,
|
||||||
|
typename std::enable_if<std::is_integral<T>::value &&
|
||||||
|
std::is_integral<U>::value>::type> {
|
||||||
|
using result_type = T;
|
||||||
|
template <typename V = result_type>
|
||||||
|
static constexpr V Do(T x, U shift) {
|
||||||
|
static_assert(!std::is_signed<U>::value, "Shift value must be unsigned.");
|
||||||
|
if (BASE_NUMERICS_LIKELY(shift < std::numeric_limits<T>::digits)) {
|
||||||
|
// Shift as unsigned to avoid undefined behavior.
|
||||||
|
V result = static_cast<V>(as_unsigned(x) << shift);
|
||||||
|
// If the shift can be reversed, we know it was valid.
|
||||||
|
if (BASE_NUMERICS_LIKELY(result >> shift == x))
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return x ? CommonMaxOrMin<V>(IsValueNegative(x)) : 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename U, class Enable = void>
|
||||||
|
struct ClampedRshOp {};
|
||||||
|
|
||||||
|
// Right shift. Negative values saturate to -1. Positive or 0 saturates to 0.
|
||||||
|
template <typename T, typename U>
|
||||||
|
struct ClampedRshOp<T,
|
||||||
|
U,
|
||||||
|
typename std::enable_if<std::is_integral<T>::value &&
|
||||||
|
std::is_integral<U>::value>::type> {
|
||||||
|
using result_type = T;
|
||||||
|
template <typename V = result_type>
|
||||||
|
static constexpr V Do(T x, U shift) {
|
||||||
|
static_assert(!std::is_signed<U>::value, "Shift value must be unsigned.");
|
||||||
|
// Signed right shift is odd, because it saturates to -1 or 0.
|
||||||
|
const V saturated = as_unsigned(V(0)) - IsValueNegative(x);
|
||||||
|
return BASE_NUMERICS_LIKELY(shift < IntegerBitsPlusSign<T>::value)
|
||||||
|
? saturated_cast<V>(x >> shift)
|
||||||
|
: saturated;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename U, class Enable = void>
|
||||||
|
struct ClampedAndOp {};
|
||||||
|
|
||||||
|
template <typename T, typename U>
|
||||||
|
struct ClampedAndOp<T,
|
||||||
|
U,
|
||||||
|
typename std::enable_if<std::is_integral<T>::value &&
|
||||||
|
std::is_integral<U>::value>::type> {
|
||||||
|
using result_type = typename std::make_unsigned<
|
||||||
|
typename MaxExponentPromotion<T, U>::type>::type;
|
||||||
|
template <typename V>
|
||||||
|
static constexpr V Do(T x, U y) {
|
||||||
|
return static_cast<result_type>(x) & static_cast<result_type>(y);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename U, class Enable = void>
|
||||||
|
struct ClampedOrOp {};
|
||||||
|
|
||||||
|
// For simplicity we promote to unsigned integers.
|
||||||
|
template <typename T, typename U>
|
||||||
|
struct ClampedOrOp<T,
|
||||||
|
U,
|
||||||
|
typename std::enable_if<std::is_integral<T>::value &&
|
||||||
|
std::is_integral<U>::value>::type> {
|
||||||
|
using result_type = typename std::make_unsigned<
|
||||||
|
typename MaxExponentPromotion<T, U>::type>::type;
|
||||||
|
template <typename V>
|
||||||
|
static constexpr V Do(T x, U y) {
|
||||||
|
return static_cast<result_type>(x) | static_cast<result_type>(y);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename U, class Enable = void>
|
||||||
|
struct ClampedXorOp {};
|
||||||
|
|
||||||
|
// For simplicity we support only unsigned integers.
|
||||||
|
template <typename T, typename U>
|
||||||
|
struct ClampedXorOp<T,
|
||||||
|
U,
|
||||||
|
typename std::enable_if<std::is_integral<T>::value &&
|
||||||
|
std::is_integral<U>::value>::type> {
|
||||||
|
using result_type = typename std::make_unsigned<
|
||||||
|
typename MaxExponentPromotion<T, U>::type>::type;
|
||||||
|
template <typename V>
|
||||||
|
static constexpr V Do(T x, U y) {
|
||||||
|
return static_cast<result_type>(x) ^ static_cast<result_type>(y);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename U, class Enable = void>
|
||||||
|
struct ClampedMaxOp {};
|
||||||
|
|
||||||
|
template <typename T, typename U>
|
||||||
|
struct ClampedMaxOp<
|
||||||
|
T,
|
||||||
|
U,
|
||||||
|
typename std::enable_if<std::is_arithmetic<T>::value &&
|
||||||
|
std::is_arithmetic<U>::value>::type> {
|
||||||
|
using result_type = typename MaxExponentPromotion<T, U>::type;
|
||||||
|
template <typename V = result_type>
|
||||||
|
static constexpr V Do(T x, U y) {
|
||||||
|
return IsGreater<T, U>::Test(x, y) ? saturated_cast<V>(x)
|
||||||
|
: saturated_cast<V>(y);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename U, class Enable = void>
|
||||||
|
struct ClampedMinOp {};
|
||||||
|
|
||||||
|
template <typename T, typename U>
|
||||||
|
struct ClampedMinOp<
|
||||||
|
T,
|
||||||
|
U,
|
||||||
|
typename std::enable_if<std::is_arithmetic<T>::value &&
|
||||||
|
std::is_arithmetic<U>::value>::type> {
|
||||||
|
using result_type = typename LowestValuePromotion<T, U>::type;
|
||||||
|
template <typename V = result_type>
|
||||||
|
static constexpr V Do(T x, U y) {
|
||||||
|
return IsLess<T, U>::Test(x, y) ? saturated_cast<V>(x)
|
||||||
|
: saturated_cast<V>(y);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// This is just boilerplate that wraps the standard floating point arithmetic.
|
||||||
|
// A macro isn't the nicest solution, but it beats rewriting these repeatedly.
|
||||||
|
#define BASE_FLOAT_ARITHMETIC_OPS(NAME, OP) \
|
||||||
|
template <typename T, typename U> \
|
||||||
|
struct Clamped##NAME##Op< \
|
||||||
|
T, U, \
|
||||||
|
typename std::enable_if<std::is_floating_point<T>::value || \
|
||||||
|
std::is_floating_point<U>::value>::type> { \
|
||||||
|
using result_type = typename MaxExponentPromotion<T, U>::type; \
|
||||||
|
template <typename V = result_type> \
|
||||||
|
static constexpr V Do(T x, U y) { \
|
||||||
|
return saturated_cast<V>(x OP y); \
|
||||||
|
} \
|
||||||
|
};
|
||||||
|
|
||||||
|
BASE_FLOAT_ARITHMETIC_OPS(Add, +)
|
||||||
|
BASE_FLOAT_ARITHMETIC_OPS(Sub, -)
|
||||||
|
BASE_FLOAT_ARITHMETIC_OPS(Mul, *)
|
||||||
|
BASE_FLOAT_ARITHMETIC_OPS(Div, /)
|
||||||
|
|
||||||
|
#undef BASE_FLOAT_ARITHMETIC_OPS
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace base
|
||||||
|
|
||||||
|
#endif // BASE_NUMERICS_CLAMPED_MATH_IMPL_H_
|
|
@ -0,0 +1,19 @@
|
||||||
|
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#ifndef BASE_NUMERICS_MATH_CONSTANTS_H_
|
||||||
|
#define BASE_NUMERICS_MATH_CONSTANTS_H_
|
||||||
|
|
||||||
|
namespace base {
|
||||||
|
|
||||||
|
constexpr double kPiDouble = 3.14159265358979323846;
|
||||||
|
constexpr float kPiFloat = 3.14159265358979323846f;
|
||||||
|
|
||||||
|
// The mean acceleration due to gravity on Earth in m/s^2.
|
||||||
|
constexpr double kMeanGravityDouble = 9.80665;
|
||||||
|
constexpr float kMeanGravityFloat = 9.80665f;
|
||||||
|
|
||||||
|
} // namespace base
|
||||||
|
|
||||||
|
#endif // BASE_NUMERICS_MATH_CONSTANTS_H_
|
|
@ -0,0 +1,27 @@
|
||||||
|
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#ifndef BASE_NUMERICS_RANGES_H_
|
||||||
|
#define BASE_NUMERICS_RANGES_H_
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
namespace base {
|
||||||
|
|
||||||
|
// To be replaced with std::clamp() from C++17, someday.
|
||||||
|
template <class T>
|
||||||
|
constexpr const T& ClampToRange(const T& value, const T& min, const T& max) {
|
||||||
|
return std::min(std::max(value, min), max);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr bool IsApproximatelyEqual(T lhs, T rhs, T tolerance) {
|
||||||
|
static_assert(std::is_arithmetic<T>::value, "Argument must be arithmetic");
|
||||||
|
return std::abs(rhs - lhs) <= tolerance;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace base
|
||||||
|
|
||||||
|
#endif // BASE_NUMERICS_RANGES_H_
|
|
@ -0,0 +1,358 @@
|
||||||
|
// Copyright 2014 The Chromium Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#ifndef BASE_NUMERICS_SAFE_CONVERSIONS_H_
|
||||||
|
#define BASE_NUMERICS_SAFE_CONVERSIONS_H_
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
#include "base/numerics/safe_conversions_impl.h"
|
||||||
|
|
||||||
|
#if !defined(__native_client__) && (defined(__ARMEL__) || defined(__arch64__))
|
||||||
|
#include "base/numerics/safe_conversions_arm_impl.h"
|
||||||
|
#define BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS (1)
|
||||||
|
#else
|
||||||
|
#define BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !BASE_NUMERICS_DISABLE_OSTREAM_OPERATORS
|
||||||
|
#include <ostream>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace base {
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
#if !BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS
|
||||||
|
template <typename Dst, typename Src>
|
||||||
|
struct SaturateFastAsmOp {
|
||||||
|
static const bool is_supported = false;
|
||||||
|
static constexpr Dst Do(Src) {
|
||||||
|
// Force a compile failure if instantiated.
|
||||||
|
return CheckOnFailure::template HandleFailure<Dst>();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#endif // BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS
|
||||||
|
#undef BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS
|
||||||
|
|
||||||
|
// The following special case a few specific integer conversions where we can
|
||||||
|
// eke out better performance than range checking.
|
||||||
|
template <typename Dst, typename Src, typename Enable = void>
|
||||||
|
struct IsValueInRangeFastOp {
|
||||||
|
static const bool is_supported = false;
|
||||||
|
static constexpr bool Do(Src value) {
|
||||||
|
// Force a compile failure if instantiated.
|
||||||
|
return CheckOnFailure::template HandleFailure<bool>();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Signed to signed range comparison.
|
||||||
|
template <typename Dst, typename Src>
|
||||||
|
struct IsValueInRangeFastOp<
|
||||||
|
Dst,
|
||||||
|
Src,
|
||||||
|
typename std::enable_if<
|
||||||
|
std::is_integral<Dst>::value && std::is_integral<Src>::value &&
|
||||||
|
std::is_signed<Dst>::value && std::is_signed<Src>::value &&
|
||||||
|
!IsTypeInRangeForNumericType<Dst, Src>::value>::type> {
|
||||||
|
static const bool is_supported = true;
|
||||||
|
|
||||||
|
static constexpr bool Do(Src value) {
|
||||||
|
// Just downcast to the smaller type, sign extend it back to the original
|
||||||
|
// type, and then see if it matches the original value.
|
||||||
|
return value == static_cast<Dst>(value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Signed to unsigned range comparison.
|
||||||
|
template <typename Dst, typename Src>
|
||||||
|
struct IsValueInRangeFastOp<
|
||||||
|
Dst,
|
||||||
|
Src,
|
||||||
|
typename std::enable_if<
|
||||||
|
std::is_integral<Dst>::value && std::is_integral<Src>::value &&
|
||||||
|
!std::is_signed<Dst>::value && std::is_signed<Src>::value &&
|
||||||
|
!IsTypeInRangeForNumericType<Dst, Src>::value>::type> {
|
||||||
|
static const bool is_supported = true;
|
||||||
|
|
||||||
|
static constexpr bool Do(Src value) {
|
||||||
|
// We cast a signed as unsigned to overflow negative values to the top,
|
||||||
|
// then compare against whichever maximum is smaller, as our upper bound.
|
||||||
|
return as_unsigned(value) <= as_unsigned(CommonMax<Src, Dst>());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Convenience function that returns true if the supplied value is in range
|
||||||
|
// for the destination type.
|
||||||
|
template <typename Dst, typename Src>
|
||||||
|
constexpr bool IsValueInRangeForNumericType(Src value) {
|
||||||
|
using SrcType = typename internal::UnderlyingType<Src>::type;
|
||||||
|
return internal::IsValueInRangeFastOp<Dst, SrcType>::is_supported
|
||||||
|
? internal::IsValueInRangeFastOp<Dst, SrcType>::Do(
|
||||||
|
static_cast<SrcType>(value))
|
||||||
|
: internal::DstRangeRelationToSrcRange<Dst>(
|
||||||
|
static_cast<SrcType>(value))
|
||||||
|
.IsValid();
|
||||||
|
}
|
||||||
|
|
||||||
|
// checked_cast<> is analogous to static_cast<> for numeric types,
|
||||||
|
// except that it CHECKs that the specified numeric conversion will not
|
||||||
|
// overflow or underflow. NaN source will always trigger a CHECK.
|
||||||
|
template <typename Dst,
|
||||||
|
class CheckHandler = internal::CheckOnFailure,
|
||||||
|
typename Src>
|
||||||
|
constexpr Dst checked_cast(Src value) {
|
||||||
|
// This throws a compile-time error on evaluating the constexpr if it can be
|
||||||
|
// determined at compile-time as failing, otherwise it will CHECK at runtime.
|
||||||
|
using SrcType = typename internal::UnderlyingType<Src>::type;
|
||||||
|
return BASE_NUMERICS_LIKELY((IsValueInRangeForNumericType<Dst>(value)))
|
||||||
|
? static_cast<Dst>(static_cast<SrcType>(value))
|
||||||
|
: CheckHandler::template HandleFailure<Dst>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default boundaries for integral/float: max/infinity, lowest/-infinity, 0/NaN.
|
||||||
|
// You may provide your own limits (e.g. to saturated_cast) so long as you
|
||||||
|
// implement all of the static constexpr member functions in the class below.
|
||||||
|
template <typename T>
|
||||||
|
struct SaturationDefaultLimits : public std::numeric_limits<T> {
|
||||||
|
static constexpr T NaN() {
|
||||||
|
return std::numeric_limits<T>::has_quiet_NaN
|
||||||
|
? std::numeric_limits<T>::quiet_NaN()
|
||||||
|
: T();
|
||||||
|
}
|
||||||
|
using std::numeric_limits<T>::max;
|
||||||
|
static constexpr T Overflow() {
|
||||||
|
return std::numeric_limits<T>::has_infinity
|
||||||
|
? std::numeric_limits<T>::infinity()
|
||||||
|
: std::numeric_limits<T>::max();
|
||||||
|
}
|
||||||
|
using std::numeric_limits<T>::lowest;
|
||||||
|
static constexpr T Underflow() {
|
||||||
|
return std::numeric_limits<T>::has_infinity
|
||||||
|
? std::numeric_limits<T>::infinity() * -1
|
||||||
|
: std::numeric_limits<T>::lowest();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Dst, template <typename> class S, typename Src>
|
||||||
|
constexpr Dst saturated_cast_impl(Src value, RangeCheck constraint) {
|
||||||
|
// For some reason clang generates much better code when the branch is
|
||||||
|
// structured exactly this way, rather than a sequence of checks.
|
||||||
|
return !constraint.IsOverflowFlagSet()
|
||||||
|
? (!constraint.IsUnderflowFlagSet() ? static_cast<Dst>(value)
|
||||||
|
: S<Dst>::Underflow())
|
||||||
|
// Skip this check for integral Src, which cannot be NaN.
|
||||||
|
: (std::is_integral<Src>::value || !constraint.IsUnderflowFlagSet()
|
||||||
|
? S<Dst>::Overflow()
|
||||||
|
: S<Dst>::NaN());
|
||||||
|
}
|
||||||
|
|
||||||
|
// We can reduce the number of conditions and get slightly better performance
|
||||||
|
// for normal signed and unsigned integer ranges. And in the specific case of
|
||||||
|
// Arm, we can use the optimized saturation instructions.
|
||||||
|
template <typename Dst, typename Src, typename Enable = void>
|
||||||
|
struct SaturateFastOp {
|
||||||
|
static const bool is_supported = false;
|
||||||
|
static constexpr Dst Do(Src value) {
|
||||||
|
// Force a compile failure if instantiated.
|
||||||
|
return CheckOnFailure::template HandleFailure<Dst>();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Dst, typename Src>
|
||||||
|
struct SaturateFastOp<
|
||||||
|
Dst,
|
||||||
|
Src,
|
||||||
|
typename std::enable_if<std::is_integral<Src>::value &&
|
||||||
|
std::is_integral<Dst>::value &&
|
||||||
|
SaturateFastAsmOp<Dst, Src>::is_supported>::type> {
|
||||||
|
static const bool is_supported = true;
|
||||||
|
static Dst Do(Src value) { return SaturateFastAsmOp<Dst, Src>::Do(value); }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Dst, typename Src>
|
||||||
|
struct SaturateFastOp<
|
||||||
|
Dst,
|
||||||
|
Src,
|
||||||
|
typename std::enable_if<std::is_integral<Src>::value &&
|
||||||
|
std::is_integral<Dst>::value &&
|
||||||
|
!SaturateFastAsmOp<Dst, Src>::is_supported>::type> {
|
||||||
|
static const bool is_supported = true;
|
||||||
|
static Dst Do(Src value) {
|
||||||
|
// The exact order of the following is structured to hit the correct
|
||||||
|
// optimization heuristics across compilers. Do not change without
|
||||||
|
// checking the emitted code.
|
||||||
|
Dst saturated = CommonMaxOrMin<Dst, Src>(
|
||||||
|
IsMaxInRangeForNumericType<Dst, Src>() ||
|
||||||
|
(!IsMinInRangeForNumericType<Dst, Src>() && IsValueNegative(value)));
|
||||||
|
return BASE_NUMERICS_LIKELY(IsValueInRangeForNumericType<Dst>(value))
|
||||||
|
? static_cast<Dst>(value)
|
||||||
|
: saturated;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// saturated_cast<> is analogous to static_cast<> for numeric types, except
|
||||||
|
// that the specified numeric conversion will saturate by default rather than
|
||||||
|
// overflow or underflow, and NaN assignment to an integral will return 0.
|
||||||
|
// All boundary condition behaviors can be overriden with a custom handler.
|
||||||
|
template <typename Dst,
|
||||||
|
template <typename> class SaturationHandler = SaturationDefaultLimits,
|
||||||
|
typename Src>
|
||||||
|
constexpr Dst saturated_cast(Src value) {
|
||||||
|
using SrcType = typename UnderlyingType<Src>::type;
|
||||||
|
return !IsCompileTimeConstant(value) &&
|
||||||
|
SaturateFastOp<Dst, SrcType>::is_supported &&
|
||||||
|
std::is_same<SaturationHandler<Dst>,
|
||||||
|
SaturationDefaultLimits<Dst>>::value
|
||||||
|
? SaturateFastOp<Dst, SrcType>::Do(static_cast<SrcType>(value))
|
||||||
|
: saturated_cast_impl<Dst, SaturationHandler, SrcType>(
|
||||||
|
static_cast<SrcType>(value),
|
||||||
|
DstRangeRelationToSrcRange<Dst, SaturationHandler, SrcType>(
|
||||||
|
static_cast<SrcType>(value)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// strict_cast<> is analogous to static_cast<> for numeric types, except that
|
||||||
|
// it will cause a compile failure if the destination type is not large enough
|
||||||
|
// to contain any value in the source type. It performs no runtime checking.
|
||||||
|
template <typename Dst, typename Src>
|
||||||
|
constexpr Dst strict_cast(Src value) {
|
||||||
|
using SrcType = typename UnderlyingType<Src>::type;
|
||||||
|
static_assert(UnderlyingType<Src>::is_numeric, "Argument must be numeric.");
|
||||||
|
static_assert(std::is_arithmetic<Dst>::value, "Result must be numeric.");
|
||||||
|
|
||||||
|
// If you got here from a compiler error, it's because you tried to assign
|
||||||
|
// from a source type to a destination type that has insufficient range.
|
||||||
|
// The solution may be to change the destination type you're assigning to,
|
||||||
|
// and use one large enough to represent the source.
|
||||||
|
// Alternatively, you may be better served with the checked_cast<> or
|
||||||
|
// saturated_cast<> template functions for your particular use case.
|
||||||
|
static_assert(StaticDstRangeRelationToSrcRange<Dst, SrcType>::value ==
|
||||||
|
NUMERIC_RANGE_CONTAINED,
|
||||||
|
"The source type is out of range for the destination type. "
|
||||||
|
"Please see strict_cast<> comments for more information.");
|
||||||
|
|
||||||
|
return static_cast<Dst>(static_cast<SrcType>(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Some wrappers to statically check that a type is in range.
|
||||||
|
template <typename Dst, typename Src, class Enable = void>
|
||||||
|
struct IsNumericRangeContained {
|
||||||
|
static const bool value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Dst, typename Src>
|
||||||
|
struct IsNumericRangeContained<
|
||||||
|
Dst,
|
||||||
|
Src,
|
||||||
|
typename std::enable_if<ArithmeticOrUnderlyingEnum<Dst>::value &&
|
||||||
|
ArithmeticOrUnderlyingEnum<Src>::value>::type> {
|
||||||
|
static const bool value = StaticDstRangeRelationToSrcRange<Dst, Src>::value ==
|
||||||
|
NUMERIC_RANGE_CONTAINED;
|
||||||
|
};
|
||||||
|
|
||||||
|
// StrictNumeric implements compile time range checking between numeric types by
|
||||||
|
// wrapping assignment operations in a strict_cast. This class is intended to be
|
||||||
|
// used for function arguments and return types, to ensure the destination type
|
||||||
|
// can always contain the source type. This is essentially the same as enforcing
|
||||||
|
// -Wconversion in gcc and C4302 warnings on MSVC, but it can be applied
|
||||||
|
// incrementally at API boundaries, making it easier to convert code so that it
|
||||||
|
// compiles cleanly with truncation warnings enabled.
|
||||||
|
// This template should introduce no runtime overhead, but it also provides no
|
||||||
|
// runtime checking of any of the associated mathematical operations. Use
|
||||||
|
// CheckedNumeric for runtime range checks of the actual value being assigned.
|
||||||
|
template <typename T>
|
||||||
|
class StrictNumeric {
|
||||||
|
public:
|
||||||
|
using type = T;
|
||||||
|
|
||||||
|
constexpr StrictNumeric() : value_(0) {}
|
||||||
|
|
||||||
|
// Copy constructor.
|
||||||
|
template <typename Src>
|
||||||
|
constexpr StrictNumeric(const StrictNumeric<Src>& rhs)
|
||||||
|
: value_(strict_cast<T>(rhs.value_)) {}
|
||||||
|
|
||||||
|
// This is not an explicit constructor because we implicitly upgrade regular
|
||||||
|
// numerics to StrictNumerics to make them easier to use.
|
||||||
|
template <typename Src>
|
||||||
|
constexpr StrictNumeric(Src value) // NOLINT(runtime/explicit)
|
||||||
|
: value_(strict_cast<T>(value)) {}
|
||||||
|
|
||||||
|
// If you got here from a compiler error, it's because you tried to assign
|
||||||
|
// from a source type to a destination type that has insufficient range.
|
||||||
|
// The solution may be to change the destination type you're assigning to,
|
||||||
|
// and use one large enough to represent the source.
|
||||||
|
// If you're assigning from a CheckedNumeric<> class, you may be able to use
|
||||||
|
// the AssignIfValid() member function, specify a narrower destination type to
|
||||||
|
// the member value functions (e.g. val.template ValueOrDie<Dst>()), use one
|
||||||
|
// of the value helper functions (e.g. ValueOrDieForType<Dst>(val)).
|
||||||
|
// If you've encountered an _ambiguous overload_ you can use a static_cast<>
|
||||||
|
// to explicitly cast the result to the destination type.
|
||||||
|
// If none of that works, you may be better served with the checked_cast<> or
|
||||||
|
// saturated_cast<> template functions for your particular use case.
|
||||||
|
template <typename Dst,
|
||||||
|
typename std::enable_if<
|
||||||
|
IsNumericRangeContained<Dst, T>::value>::type* = nullptr>
|
||||||
|
constexpr operator Dst() const {
|
||||||
|
return static_cast<typename ArithmeticOrUnderlyingEnum<Dst>::type>(value_);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const T value_;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Convience wrapper returns a StrictNumeric from the provided arithmetic type.
|
||||||
|
template <typename T>
|
||||||
|
constexpr StrictNumeric<typename UnderlyingType<T>::type> MakeStrictNum(
|
||||||
|
const T value) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !BASE_NUMERICS_DISABLE_OSTREAM_OPERATORS
|
||||||
|
// Overload the ostream output operator to make logging work nicely.
|
||||||
|
template <typename T>
|
||||||
|
std::ostream& operator<<(std::ostream& os, const StrictNumeric<T>& value) {
|
||||||
|
os << static_cast<T>(value);
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define BASE_NUMERIC_COMPARISON_OPERATORS(CLASS, NAME, OP) \
|
||||||
|
template <typename L, typename R, \
|
||||||
|
typename std::enable_if< \
|
||||||
|
internal::Is##CLASS##Op<L, R>::value>::type* = nullptr> \
|
||||||
|
constexpr bool operator OP(const L lhs, const R rhs) { \
|
||||||
|
return SafeCompare<NAME, typename UnderlyingType<L>::type, \
|
||||||
|
typename UnderlyingType<R>::type>(lhs, rhs); \
|
||||||
|
}
|
||||||
|
|
||||||
|
BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsLess, <)
|
||||||
|
BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsLessOrEqual, <=)
|
||||||
|
BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsGreater, >)
|
||||||
|
BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsGreaterOrEqual, >=)
|
||||||
|
BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsEqual, ==)
|
||||||
|
BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsNotEqual, !=)
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
using internal::as_signed;
|
||||||
|
using internal::as_unsigned;
|
||||||
|
using internal::checked_cast;
|
||||||
|
using internal::strict_cast;
|
||||||
|
using internal::saturated_cast;
|
||||||
|
using internal::SafeUnsignedAbs;
|
||||||
|
using internal::StrictNumeric;
|
||||||
|
using internal::MakeStrictNum;
|
||||||
|
using internal::IsValueInRangeForNumericType;
|
||||||
|
using internal::IsTypeInRangeForNumericType;
|
||||||
|
using internal::IsValueNegative;
|
||||||
|
|
||||||
|
// Explicitly make a shorter size_t alias for convenience.
|
||||||
|
using SizeT = StrictNumeric<size_t>;
|
||||||
|
|
||||||
|
} // namespace base
|
||||||
|
|
||||||
|
#endif // BASE_NUMERICS_SAFE_CONVERSIONS_H_
|
|
@ -0,0 +1,51 @@
|
||||||
|
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#ifndef BASE_NUMERICS_SAFE_CONVERSIONS_ARM_IMPL_H_
|
||||||
|
#define BASE_NUMERICS_SAFE_CONVERSIONS_ARM_IMPL_H_
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <limits>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
#include "base/numerics/safe_conversions_impl.h"
|
||||||
|
|
||||||
|
namespace base {
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
// Fast saturation to a destination type.
|
||||||
|
template <typename Dst, typename Src>
|
||||||
|
struct SaturateFastAsmOp {
|
||||||
|
static constexpr bool is_supported =
|
||||||
|
std::is_signed<Src>::value && std::is_integral<Dst>::value &&
|
||||||
|
std::is_integral<Src>::value &&
|
||||||
|
IntegerBitsPlusSign<Src>::value <= IntegerBitsPlusSign<int32_t>::value &&
|
||||||
|
IntegerBitsPlusSign<Dst>::value <= IntegerBitsPlusSign<int32_t>::value &&
|
||||||
|
!IsTypeInRangeForNumericType<Dst, Src>::value;
|
||||||
|
|
||||||
|
__attribute__((always_inline)) static Dst Do(Src value) {
|
||||||
|
int32_t src = value;
|
||||||
|
typename std::conditional<std::is_signed<Dst>::value, int32_t,
|
||||||
|
uint32_t>::type result;
|
||||||
|
if (std::is_signed<Dst>::value) {
|
||||||
|
asm("ssat %[dst], %[shift], %[src]"
|
||||||
|
: [dst] "=r"(result)
|
||||||
|
: [src] "r"(src), [shift] "n"(IntegerBitsPlusSign<Dst>::value <= 32
|
||||||
|
? IntegerBitsPlusSign<Dst>::value
|
||||||
|
: 32));
|
||||||
|
} else {
|
||||||
|
asm("usat %[dst], %[shift], %[src]"
|
||||||
|
: [dst] "=r"(result)
|
||||||
|
: [src] "r"(src), [shift] "n"(IntegerBitsPlusSign<Dst>::value < 32
|
||||||
|
? IntegerBitsPlusSign<Dst>::value
|
||||||
|
: 31));
|
||||||
|
}
|
||||||
|
return static_cast<Dst>(result);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace base
|
||||||
|
|
||||||
|
#endif // BASE_NUMERICS_SAFE_CONVERSIONS_ARM_IMPL_H_
|
|
@ -0,0 +1,850 @@
|
||||||
|
// Copyright 2014 The Chromium Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#ifndef BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_
|
||||||
|
#define BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
#if defined(__GNUC__) || defined(__clang__)
|
||||||
|
#define BASE_NUMERICS_LIKELY(x) __builtin_expect(!!(x), 1)
|
||||||
|
#define BASE_NUMERICS_UNLIKELY(x) __builtin_expect(!!(x), 0)
|
||||||
|
#else
|
||||||
|
#define BASE_NUMERICS_LIKELY(x) (x)
|
||||||
|
#define BASE_NUMERICS_UNLIKELY(x) (x)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace base {
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
// The std library doesn't provide a binary max_exponent for integers, however
|
||||||
|
// we can compute an analog using std::numeric_limits<>::digits.
|
||||||
|
template <typename NumericType>
|
||||||
|
struct MaxExponent {
|
||||||
|
static const int value = std::is_floating_point<NumericType>::value
|
||||||
|
? std::numeric_limits<NumericType>::max_exponent
|
||||||
|
: std::numeric_limits<NumericType>::digits + 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
// The number of bits (including the sign) in an integer. Eliminates sizeof
|
||||||
|
// hacks.
|
||||||
|
template <typename NumericType>
|
||||||
|
struct IntegerBitsPlusSign {
|
||||||
|
static const int value = std::numeric_limits<NumericType>::digits +
|
||||||
|
std::is_signed<NumericType>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Helper templates for integer manipulations.
|
||||||
|
|
||||||
|
template <typename Integer>
|
||||||
|
struct PositionOfSignBit {
|
||||||
|
static const size_t value = IntegerBitsPlusSign<Integer>::value - 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Determines if a numeric value is negative without throwing compiler
|
||||||
|
// warnings on: unsigned(value) < 0.
|
||||||
|
template <typename T,
|
||||||
|
typename std::enable_if<std::is_signed<T>::value>::type* = nullptr>
|
||||||
|
constexpr bool IsValueNegative(T value) {
|
||||||
|
static_assert(std::is_arithmetic<T>::value, "Argument must be numeric.");
|
||||||
|
return value < 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T,
|
||||||
|
typename std::enable_if<!std::is_signed<T>::value>::type* = nullptr>
|
||||||
|
constexpr bool IsValueNegative(T) {
|
||||||
|
static_assert(std::is_arithmetic<T>::value, "Argument must be numeric.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This performs a fast negation, returning a signed value. It works on unsigned
|
||||||
|
// arguments, but probably doesn't do what you want for any unsigned value
|
||||||
|
// larger than max / 2 + 1 (i.e. signed min cast to unsigned).
|
||||||
|
template <typename T>
|
||||||
|
constexpr typename std::make_signed<T>::type ConditionalNegate(
|
||||||
|
T x,
|
||||||
|
bool is_negative) {
|
||||||
|
static_assert(std::is_integral<T>::value, "Type must be integral");
|
||||||
|
using SignedT = typename std::make_signed<T>::type;
|
||||||
|
using UnsignedT = typename std::make_unsigned<T>::type;
|
||||||
|
return static_cast<SignedT>(
|
||||||
|
(static_cast<UnsignedT>(x) ^ -SignedT(is_negative)) + is_negative);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This performs a safe, absolute value via unsigned overflow.
|
||||||
|
template <typename T>
|
||||||
|
constexpr typename std::make_unsigned<T>::type SafeUnsignedAbs(T value) {
|
||||||
|
static_assert(std::is_integral<T>::value, "Type must be integral");
|
||||||
|
using UnsignedT = typename std::make_unsigned<T>::type;
|
||||||
|
return IsValueNegative(value) ? 0 - static_cast<UnsignedT>(value)
|
||||||
|
: static_cast<UnsignedT>(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This allows us to switch paths on known compile-time constants.
|
||||||
|
#if defined(__clang__) || defined(__GNUC__)
|
||||||
|
constexpr bool CanDetectCompileTimeConstant() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
template <typename T>
|
||||||
|
constexpr bool IsCompileTimeConstant(const T v) {
|
||||||
|
return __builtin_constant_p(v);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
constexpr bool CanDetectCompileTimeConstant() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
template <typename T>
|
||||||
|
constexpr bool IsCompileTimeConstant(const T) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
template <typename T>
|
||||||
|
constexpr bool MustTreatAsConstexpr(const T v) {
|
||||||
|
// Either we can't detect a compile-time constant, and must always use the
|
||||||
|
// constexpr path, or we know we have a compile-time constant.
|
||||||
|
return !CanDetectCompileTimeConstant() || IsCompileTimeConstant(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Forces a crash, like a CHECK(false). Used for numeric boundary errors.
|
||||||
|
// Also used in a constexpr template to trigger a compilation failure on
|
||||||
|
// an error condition.
|
||||||
|
struct CheckOnFailure {
|
||||||
|
template <typename T>
|
||||||
|
static T HandleFailure() {
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
__debugbreak();
|
||||||
|
#elif defined(__GNUC__) || defined(__clang__)
|
||||||
|
__builtin_trap();
|
||||||
|
#else
|
||||||
|
((void)(*(volatile char*)0 = 0));
|
||||||
|
#endif
|
||||||
|
return T();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
enum IntegerRepresentation {
|
||||||
|
INTEGER_REPRESENTATION_UNSIGNED,
|
||||||
|
INTEGER_REPRESENTATION_SIGNED
|
||||||
|
};
|
||||||
|
|
||||||
|
// A range for a given nunmeric Src type is contained for a given numeric Dst
|
||||||
|
// type if both numeric_limits<Src>::max() <= numeric_limits<Dst>::max() and
|
||||||
|
// numeric_limits<Src>::lowest() >= numeric_limits<Dst>::lowest() are true.
|
||||||
|
// We implement this as template specializations rather than simple static
|
||||||
|
// comparisons to ensure type correctness in our comparisons.
|
||||||
|
enum NumericRangeRepresentation {
|
||||||
|
NUMERIC_RANGE_NOT_CONTAINED,
|
||||||
|
NUMERIC_RANGE_CONTAINED
|
||||||
|
};
|
||||||
|
|
||||||
|
// Helper templates to statically determine if our destination type can contain
|
||||||
|
// maximum and minimum values represented by the source type.
|
||||||
|
|
||||||
|
template <typename Dst,
|
||||||
|
typename Src,
|
||||||
|
IntegerRepresentation DstSign = std::is_signed<Dst>::value
|
||||||
|
? INTEGER_REPRESENTATION_SIGNED
|
||||||
|
: INTEGER_REPRESENTATION_UNSIGNED,
|
||||||
|
IntegerRepresentation SrcSign = std::is_signed<Src>::value
|
||||||
|
? INTEGER_REPRESENTATION_SIGNED
|
||||||
|
: INTEGER_REPRESENTATION_UNSIGNED>
|
||||||
|
struct StaticDstRangeRelationToSrcRange;
|
||||||
|
|
||||||
|
// Same sign: Dst is guaranteed to contain Src only if its range is equal or
|
||||||
|
// larger.
|
||||||
|
template <typename Dst, typename Src, IntegerRepresentation Sign>
|
||||||
|
struct StaticDstRangeRelationToSrcRange<Dst, Src, Sign, Sign> {
|
||||||
|
static const NumericRangeRepresentation value =
|
||||||
|
MaxExponent<Dst>::value >= MaxExponent<Src>::value
|
||||||
|
? NUMERIC_RANGE_CONTAINED
|
||||||
|
: NUMERIC_RANGE_NOT_CONTAINED;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Unsigned to signed: Dst is guaranteed to contain source only if its range is
|
||||||
|
// larger.
|
||||||
|
template <typename Dst, typename Src>
|
||||||
|
struct StaticDstRangeRelationToSrcRange<Dst,
|
||||||
|
Src,
|
||||||
|
INTEGER_REPRESENTATION_SIGNED,
|
||||||
|
INTEGER_REPRESENTATION_UNSIGNED> {
|
||||||
|
static const NumericRangeRepresentation value =
|
||||||
|
MaxExponent<Dst>::value > MaxExponent<Src>::value
|
||||||
|
? NUMERIC_RANGE_CONTAINED
|
||||||
|
: NUMERIC_RANGE_NOT_CONTAINED;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Signed to unsigned: Dst cannot be statically determined to contain Src.
|
||||||
|
template <typename Dst, typename Src>
|
||||||
|
struct StaticDstRangeRelationToSrcRange<Dst,
|
||||||
|
Src,
|
||||||
|
INTEGER_REPRESENTATION_UNSIGNED,
|
||||||
|
INTEGER_REPRESENTATION_SIGNED> {
|
||||||
|
static const NumericRangeRepresentation value = NUMERIC_RANGE_NOT_CONTAINED;
|
||||||
|
};
|
||||||
|
|
||||||
|
// This class wraps the range constraints as separate booleans so the compiler
|
||||||
|
// can identify constants and eliminate unused code paths.
|
||||||
|
class RangeCheck {
|
||||||
|
public:
|
||||||
|
constexpr RangeCheck(bool is_in_lower_bound, bool is_in_upper_bound)
|
||||||
|
: is_underflow_(!is_in_lower_bound), is_overflow_(!is_in_upper_bound) {}
|
||||||
|
constexpr RangeCheck() : is_underflow_(0), is_overflow_(0) {}
|
||||||
|
constexpr bool IsValid() const { return !is_overflow_ && !is_underflow_; }
|
||||||
|
constexpr bool IsInvalid() const { return is_overflow_ && is_underflow_; }
|
||||||
|
constexpr bool IsOverflow() const { return is_overflow_ && !is_underflow_; }
|
||||||
|
constexpr bool IsUnderflow() const { return !is_overflow_ && is_underflow_; }
|
||||||
|
constexpr bool IsOverflowFlagSet() const { return is_overflow_; }
|
||||||
|
constexpr bool IsUnderflowFlagSet() const { return is_underflow_; }
|
||||||
|
constexpr bool operator==(const RangeCheck rhs) const {
|
||||||
|
return is_underflow_ == rhs.is_underflow_ &&
|
||||||
|
is_overflow_ == rhs.is_overflow_;
|
||||||
|
}
|
||||||
|
constexpr bool operator!=(const RangeCheck rhs) const {
|
||||||
|
return !(*this == rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Do not change the order of these member variables. The integral conversion
|
||||||
|
// optimization depends on this exact order.
|
||||||
|
const bool is_underflow_;
|
||||||
|
const bool is_overflow_;
|
||||||
|
};
|
||||||
|
|
||||||
|
// The following helper template addresses a corner case in range checks for
|
||||||
|
// conversion from a floating-point type to an integral type of smaller range
|
||||||
|
// but larger precision (e.g. float -> unsigned). The problem is as follows:
|
||||||
|
// 1. Integral maximum is always one less than a power of two, so it must be
|
||||||
|
// truncated to fit the mantissa of the floating point. The direction of
|
||||||
|
// rounding is implementation defined, but by default it's always IEEE
|
||||||
|
// floats, which round to nearest and thus result in a value of larger
|
||||||
|
// magnitude than the integral value.
|
||||||
|
// Example: float f = UINT_MAX; // f is 4294967296f but UINT_MAX
|
||||||
|
// // is 4294967295u.
|
||||||
|
// 2. If the floating point value is equal to the promoted integral maximum
|
||||||
|
// value, a range check will erroneously pass.
|
||||||
|
// Example: (4294967296f <= 4294967295u) // This is true due to a precision
|
||||||
|
// // loss in rounding up to float.
|
||||||
|
// 3. When the floating point value is then converted to an integral, the
|
||||||
|
// resulting value is out of range for the target integral type and
|
||||||
|
// thus is implementation defined.
|
||||||
|
// Example: unsigned u = (float)INT_MAX; // u will typically overflow to 0.
|
||||||
|
// To fix this bug we manually truncate the maximum value when the destination
|
||||||
|
// type is an integral of larger precision than the source floating-point type,
|
||||||
|
// such that the resulting maximum is represented exactly as a floating point.
|
||||||
|
template <typename Dst, typename Src, template <typename> class Bounds>
|
||||||
|
struct NarrowingRange {
|
||||||
|
using SrcLimits = std::numeric_limits<Src>;
|
||||||
|
using DstLimits = typename std::numeric_limits<Dst>;
|
||||||
|
|
||||||
|
// Computes the mask required to make an accurate comparison between types.
|
||||||
|
static const int kShift =
|
||||||
|
(MaxExponent<Src>::value > MaxExponent<Dst>::value &&
|
||||||
|
SrcLimits::digits < DstLimits::digits)
|
||||||
|
? (DstLimits::digits - SrcLimits::digits)
|
||||||
|
: 0;
|
||||||
|
template <
|
||||||
|
typename T,
|
||||||
|
typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
|
||||||
|
|
||||||
|
// Masks out the integer bits that are beyond the precision of the
|
||||||
|
// intermediate type used for comparison.
|
||||||
|
static constexpr T Adjust(T value) {
|
||||||
|
static_assert(std::is_same<T, Dst>::value, "");
|
||||||
|
static_assert(kShift < DstLimits::digits, "");
|
||||||
|
return static_cast<T>(
|
||||||
|
ConditionalNegate(SafeUnsignedAbs(value) & ~((T(1) << kShift) - T(1)),
|
||||||
|
IsValueNegative(value)));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T,
|
||||||
|
typename std::enable_if<std::is_floating_point<T>::value>::type* =
|
||||||
|
nullptr>
|
||||||
|
static constexpr T Adjust(T value) {
|
||||||
|
static_assert(std::is_same<T, Dst>::value, "");
|
||||||
|
static_assert(kShift == 0, "");
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr Dst max() { return Adjust(Bounds<Dst>::max()); }
|
||||||
|
static constexpr Dst lowest() { return Adjust(Bounds<Dst>::lowest()); }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Dst,
|
||||||
|
typename Src,
|
||||||
|
template <typename> class Bounds,
|
||||||
|
IntegerRepresentation DstSign = std::is_signed<Dst>::value
|
||||||
|
? INTEGER_REPRESENTATION_SIGNED
|
||||||
|
: INTEGER_REPRESENTATION_UNSIGNED,
|
||||||
|
IntegerRepresentation SrcSign = std::is_signed<Src>::value
|
||||||
|
? INTEGER_REPRESENTATION_SIGNED
|
||||||
|
: INTEGER_REPRESENTATION_UNSIGNED,
|
||||||
|
NumericRangeRepresentation DstRange =
|
||||||
|
StaticDstRangeRelationToSrcRange<Dst, Src>::value>
|
||||||
|
struct DstRangeRelationToSrcRangeImpl;
|
||||||
|
|
||||||
|
// The following templates are for ranges that must be verified at runtime. We
|
||||||
|
// split it into checks based on signedness to avoid confusing casts and
|
||||||
|
// compiler warnings on signed an unsigned comparisons.
|
||||||
|
|
||||||
|
// Same sign narrowing: The range is contained for normal limits.
|
||||||
|
template <typename Dst,
|
||||||
|
typename Src,
|
||||||
|
template <typename> class Bounds,
|
||||||
|
IntegerRepresentation DstSign,
|
||||||
|
IntegerRepresentation SrcSign>
|
||||||
|
struct DstRangeRelationToSrcRangeImpl<Dst,
|
||||||
|
Src,
|
||||||
|
Bounds,
|
||||||
|
DstSign,
|
||||||
|
SrcSign,
|
||||||
|
NUMERIC_RANGE_CONTAINED> {
|
||||||
|
static constexpr RangeCheck Check(Src value) {
|
||||||
|
using SrcLimits = std::numeric_limits<Src>;
|
||||||
|
using DstLimits = NarrowingRange<Dst, Src, Bounds>;
|
||||||
|
return RangeCheck(
|
||||||
|
static_cast<Dst>(SrcLimits::lowest()) >= DstLimits::lowest() ||
|
||||||
|
static_cast<Dst>(value) >= DstLimits::lowest(),
|
||||||
|
static_cast<Dst>(SrcLimits::max()) <= DstLimits::max() ||
|
||||||
|
static_cast<Dst>(value) <= DstLimits::max());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Signed to signed narrowing: Both the upper and lower boundaries may be
|
||||||
|
// exceeded for standard limits.
|
||||||
|
template <typename Dst, typename Src, template <typename> class Bounds>
|
||||||
|
struct DstRangeRelationToSrcRangeImpl<Dst,
|
||||||
|
Src,
|
||||||
|
Bounds,
|
||||||
|
INTEGER_REPRESENTATION_SIGNED,
|
||||||
|
INTEGER_REPRESENTATION_SIGNED,
|
||||||
|
NUMERIC_RANGE_NOT_CONTAINED> {
|
||||||
|
static constexpr RangeCheck Check(Src value) {
|
||||||
|
using DstLimits = NarrowingRange<Dst, Src, Bounds>;
|
||||||
|
return RangeCheck(value >= DstLimits::lowest(), value <= DstLimits::max());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Unsigned to unsigned narrowing: Only the upper bound can be exceeded for
|
||||||
|
// standard limits.
|
||||||
|
template <typename Dst, typename Src, template <typename> class Bounds>
|
||||||
|
struct DstRangeRelationToSrcRangeImpl<Dst,
|
||||||
|
Src,
|
||||||
|
Bounds,
|
||||||
|
INTEGER_REPRESENTATION_UNSIGNED,
|
||||||
|
INTEGER_REPRESENTATION_UNSIGNED,
|
||||||
|
NUMERIC_RANGE_NOT_CONTAINED> {
|
||||||
|
static constexpr RangeCheck Check(Src value) {
|
||||||
|
using DstLimits = NarrowingRange<Dst, Src, Bounds>;
|
||||||
|
return RangeCheck(
|
||||||
|
DstLimits::lowest() == Dst(0) || value >= DstLimits::lowest(),
|
||||||
|
value <= DstLimits::max());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Unsigned to signed: Only the upper bound can be exceeded for standard limits.
|
||||||
|
template <typename Dst, typename Src, template <typename> class Bounds>
|
||||||
|
struct DstRangeRelationToSrcRangeImpl<Dst,
|
||||||
|
Src,
|
||||||
|
Bounds,
|
||||||
|
INTEGER_REPRESENTATION_SIGNED,
|
||||||
|
INTEGER_REPRESENTATION_UNSIGNED,
|
||||||
|
NUMERIC_RANGE_NOT_CONTAINED> {
|
||||||
|
static constexpr RangeCheck Check(Src value) {
|
||||||
|
using DstLimits = NarrowingRange<Dst, Src, Bounds>;
|
||||||
|
using Promotion = decltype(Src() + Dst());
|
||||||
|
return RangeCheck(DstLimits::lowest() <= Dst(0) ||
|
||||||
|
static_cast<Promotion>(value) >=
|
||||||
|
static_cast<Promotion>(DstLimits::lowest()),
|
||||||
|
static_cast<Promotion>(value) <=
|
||||||
|
static_cast<Promotion>(DstLimits::max()));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Signed to unsigned: The upper boundary may be exceeded for a narrower Dst,
|
||||||
|
// and any negative value exceeds the lower boundary for standard limits.
|
||||||
|
template <typename Dst, typename Src, template <typename> class Bounds>
|
||||||
|
struct DstRangeRelationToSrcRangeImpl<Dst,
|
||||||
|
Src,
|
||||||
|
Bounds,
|
||||||
|
INTEGER_REPRESENTATION_UNSIGNED,
|
||||||
|
INTEGER_REPRESENTATION_SIGNED,
|
||||||
|
NUMERIC_RANGE_NOT_CONTAINED> {
|
||||||
|
static constexpr RangeCheck Check(Src value) {
|
||||||
|
using SrcLimits = std::numeric_limits<Src>;
|
||||||
|
using DstLimits = NarrowingRange<Dst, Src, Bounds>;
|
||||||
|
using Promotion = decltype(Src() + Dst());
|
||||||
|
return RangeCheck(
|
||||||
|
value >= Src(0) && (DstLimits::lowest() == 0 ||
|
||||||
|
static_cast<Dst>(value) >= DstLimits::lowest()),
|
||||||
|
static_cast<Promotion>(SrcLimits::max()) <=
|
||||||
|
static_cast<Promotion>(DstLimits::max()) ||
|
||||||
|
static_cast<Promotion>(value) <=
|
||||||
|
static_cast<Promotion>(DstLimits::max()));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Simple wrapper for statically checking if a type's range is contained.
|
||||||
|
template <typename Dst, typename Src>
|
||||||
|
struct IsTypeInRangeForNumericType {
|
||||||
|
static const bool value = StaticDstRangeRelationToSrcRange<Dst, Src>::value ==
|
||||||
|
NUMERIC_RANGE_CONTAINED;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Dst,
|
||||||
|
template <typename> class Bounds = std::numeric_limits,
|
||||||
|
typename Src>
|
||||||
|
constexpr RangeCheck DstRangeRelationToSrcRange(Src value) {
|
||||||
|
static_assert(std::is_arithmetic<Src>::value, "Argument must be numeric.");
|
||||||
|
static_assert(std::is_arithmetic<Dst>::value, "Result must be numeric.");
|
||||||
|
static_assert(Bounds<Dst>::lowest() < Bounds<Dst>::max(), "");
|
||||||
|
return DstRangeRelationToSrcRangeImpl<Dst, Src, Bounds>::Check(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Integer promotion templates used by the portable checked integer arithmetic.
|
||||||
|
template <size_t Size, bool IsSigned>
|
||||||
|
struct IntegerForDigitsAndSign;
|
||||||
|
|
||||||
|
#define INTEGER_FOR_DIGITS_AND_SIGN(I) \
|
||||||
|
template <> \
|
||||||
|
struct IntegerForDigitsAndSign<IntegerBitsPlusSign<I>::value, \
|
||||||
|
std::is_signed<I>::value> { \
|
||||||
|
using type = I; \
|
||||||
|
}
|
||||||
|
|
||||||
|
INTEGER_FOR_DIGITS_AND_SIGN(int8_t);
|
||||||
|
INTEGER_FOR_DIGITS_AND_SIGN(uint8_t);
|
||||||
|
INTEGER_FOR_DIGITS_AND_SIGN(int16_t);
|
||||||
|
INTEGER_FOR_DIGITS_AND_SIGN(uint16_t);
|
||||||
|
INTEGER_FOR_DIGITS_AND_SIGN(int32_t);
|
||||||
|
INTEGER_FOR_DIGITS_AND_SIGN(uint32_t);
|
||||||
|
INTEGER_FOR_DIGITS_AND_SIGN(int64_t);
|
||||||
|
INTEGER_FOR_DIGITS_AND_SIGN(uint64_t);
|
||||||
|
#undef INTEGER_FOR_DIGITS_AND_SIGN
|
||||||
|
|
||||||
|
// WARNING: We have no IntegerForSizeAndSign<16, *>. If we ever add one to
|
||||||
|
// support 128-bit math, then the ArithmeticPromotion template below will need
|
||||||
|
// to be updated (or more likely replaced with a decltype expression).
|
||||||
|
static_assert(IntegerBitsPlusSign<intmax_t>::value == 64,
|
||||||
|
"Max integer size not supported for this toolchain.");
|
||||||
|
|
||||||
|
template <typename Integer, bool IsSigned = std::is_signed<Integer>::value>
|
||||||
|
struct TwiceWiderInteger {
|
||||||
|
using type =
|
||||||
|
typename IntegerForDigitsAndSign<IntegerBitsPlusSign<Integer>::value * 2,
|
||||||
|
IsSigned>::type;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ArithmeticPromotionCategory {
|
||||||
|
LEFT_PROMOTION, // Use the type of the left-hand argument.
|
||||||
|
RIGHT_PROMOTION // Use the type of the right-hand argument.
|
||||||
|
};
|
||||||
|
|
||||||
|
// Determines the type that can represent the largest positive value.
|
||||||
|
template <typename Lhs,
|
||||||
|
typename Rhs,
|
||||||
|
ArithmeticPromotionCategory Promotion =
|
||||||
|
(MaxExponent<Lhs>::value > MaxExponent<Rhs>::value)
|
||||||
|
? LEFT_PROMOTION
|
||||||
|
: RIGHT_PROMOTION>
|
||||||
|
struct MaxExponentPromotion;
|
||||||
|
|
||||||
|
template <typename Lhs, typename Rhs>
|
||||||
|
struct MaxExponentPromotion<Lhs, Rhs, LEFT_PROMOTION> {
|
||||||
|
using type = Lhs;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Lhs, typename Rhs>
|
||||||
|
struct MaxExponentPromotion<Lhs, Rhs, RIGHT_PROMOTION> {
|
||||||
|
using type = Rhs;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Determines the type that can represent the lowest arithmetic value.
|
||||||
|
template <typename Lhs,
|
||||||
|
typename Rhs,
|
||||||
|
ArithmeticPromotionCategory Promotion =
|
||||||
|
std::is_signed<Lhs>::value
|
||||||
|
? (std::is_signed<Rhs>::value
|
||||||
|
? (MaxExponent<Lhs>::value > MaxExponent<Rhs>::value
|
||||||
|
? LEFT_PROMOTION
|
||||||
|
: RIGHT_PROMOTION)
|
||||||
|
: LEFT_PROMOTION)
|
||||||
|
: (std::is_signed<Rhs>::value
|
||||||
|
? RIGHT_PROMOTION
|
||||||
|
: (MaxExponent<Lhs>::value < MaxExponent<Rhs>::value
|
||||||
|
? LEFT_PROMOTION
|
||||||
|
: RIGHT_PROMOTION))>
|
||||||
|
struct LowestValuePromotion;
|
||||||
|
|
||||||
|
template <typename Lhs, typename Rhs>
|
||||||
|
struct LowestValuePromotion<Lhs, Rhs, LEFT_PROMOTION> {
|
||||||
|
using type = Lhs;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Lhs, typename Rhs>
|
||||||
|
struct LowestValuePromotion<Lhs, Rhs, RIGHT_PROMOTION> {
|
||||||
|
using type = Rhs;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Determines the type that is best able to represent an arithmetic result.
|
||||||
|
template <
|
||||||
|
typename Lhs,
|
||||||
|
typename Rhs = Lhs,
|
||||||
|
bool is_intmax_type =
|
||||||
|
std::is_integral<typename MaxExponentPromotion<Lhs, Rhs>::type>::value&&
|
||||||
|
IntegerBitsPlusSign<typename MaxExponentPromotion<Lhs, Rhs>::type>::
|
||||||
|
value == IntegerBitsPlusSign<intmax_t>::value,
|
||||||
|
bool is_max_exponent =
|
||||||
|
StaticDstRangeRelationToSrcRange<
|
||||||
|
typename MaxExponentPromotion<Lhs, Rhs>::type,
|
||||||
|
Lhs>::value ==
|
||||||
|
NUMERIC_RANGE_CONTAINED&& StaticDstRangeRelationToSrcRange<
|
||||||
|
typename MaxExponentPromotion<Lhs, Rhs>::type,
|
||||||
|
Rhs>::value == NUMERIC_RANGE_CONTAINED>
|
||||||
|
struct BigEnoughPromotion;
|
||||||
|
|
||||||
|
// The side with the max exponent is big enough.
|
||||||
|
template <typename Lhs, typename Rhs, bool is_intmax_type>
|
||||||
|
struct BigEnoughPromotion<Lhs, Rhs, is_intmax_type, true> {
|
||||||
|
using type = typename MaxExponentPromotion<Lhs, Rhs>::type;
|
||||||
|
static const bool is_contained = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// We can use a twice wider type to fit.
|
||||||
|
template <typename Lhs, typename Rhs>
|
||||||
|
struct BigEnoughPromotion<Lhs, Rhs, false, false> {
|
||||||
|
using type =
|
||||||
|
typename TwiceWiderInteger<typename MaxExponentPromotion<Lhs, Rhs>::type,
|
||||||
|
std::is_signed<Lhs>::value ||
|
||||||
|
std::is_signed<Rhs>::value>::type;
|
||||||
|
static const bool is_contained = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// No type is large enough.
|
||||||
|
template <typename Lhs, typename Rhs>
|
||||||
|
struct BigEnoughPromotion<Lhs, Rhs, true, false> {
|
||||||
|
using type = typename MaxExponentPromotion<Lhs, Rhs>::type;
|
||||||
|
static const bool is_contained = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
// We can statically check if operations on the provided types can wrap, so we
|
||||||
|
// can skip the checked operations if they're not needed. So, for an integer we
|
||||||
|
// care if the destination type preserves the sign and is twice the width of
|
||||||
|
// the source.
|
||||||
|
template <typename T, typename Lhs, typename Rhs = Lhs>
|
||||||
|
struct IsIntegerArithmeticSafe {
|
||||||
|
static const bool value =
|
||||||
|
!std::is_floating_point<T>::value &&
|
||||||
|
!std::is_floating_point<Lhs>::value &&
|
||||||
|
!std::is_floating_point<Rhs>::value &&
|
||||||
|
std::is_signed<T>::value >= std::is_signed<Lhs>::value &&
|
||||||
|
IntegerBitsPlusSign<T>::value >= (2 * IntegerBitsPlusSign<Lhs>::value) &&
|
||||||
|
std::is_signed<T>::value >= std::is_signed<Rhs>::value &&
|
||||||
|
IntegerBitsPlusSign<T>::value >= (2 * IntegerBitsPlusSign<Rhs>::value);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Promotes to a type that can represent any possible result of a binary
|
||||||
|
// arithmetic operation with the source types.
|
||||||
|
template <typename Lhs,
|
||||||
|
typename Rhs,
|
||||||
|
bool is_promotion_possible = IsIntegerArithmeticSafe<
|
||||||
|
typename std::conditional<std::is_signed<Lhs>::value ||
|
||||||
|
std::is_signed<Rhs>::value,
|
||||||
|
intmax_t,
|
||||||
|
uintmax_t>::type,
|
||||||
|
typename MaxExponentPromotion<Lhs, Rhs>::type>::value>
|
||||||
|
struct FastIntegerArithmeticPromotion;
|
||||||
|
|
||||||
|
template <typename Lhs, typename Rhs>
|
||||||
|
struct FastIntegerArithmeticPromotion<Lhs, Rhs, true> {
|
||||||
|
using type =
|
||||||
|
typename TwiceWiderInteger<typename MaxExponentPromotion<Lhs, Rhs>::type,
|
||||||
|
std::is_signed<Lhs>::value ||
|
||||||
|
std::is_signed<Rhs>::value>::type;
|
||||||
|
static_assert(IsIntegerArithmeticSafe<type, Lhs, Rhs>::value, "");
|
||||||
|
static const bool is_contained = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Lhs, typename Rhs>
|
||||||
|
struct FastIntegerArithmeticPromotion<Lhs, Rhs, false> {
|
||||||
|
using type = typename BigEnoughPromotion<Lhs, Rhs>::type;
|
||||||
|
static const bool is_contained = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Extracts the underlying type from an enum.
|
||||||
|
template <typename T, bool is_enum = std::is_enum<T>::value>
|
||||||
|
struct ArithmeticOrUnderlyingEnum;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct ArithmeticOrUnderlyingEnum<T, true> {
|
||||||
|
using type = typename std::underlying_type<T>::type;
|
||||||
|
static const bool value = std::is_arithmetic<type>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct ArithmeticOrUnderlyingEnum<T, false> {
|
||||||
|
using type = T;
|
||||||
|
static const bool value = std::is_arithmetic<type>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
// The following are helper templates used in the CheckedNumeric class.
|
||||||
|
template <typename T>
|
||||||
|
class CheckedNumeric;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class ClampedNumeric;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class StrictNumeric;
|
||||||
|
|
||||||
|
// Used to treat CheckedNumeric and arithmetic underlying types the same.
|
||||||
|
template <typename T>
|
||||||
|
struct UnderlyingType {
|
||||||
|
using type = typename ArithmeticOrUnderlyingEnum<T>::type;
|
||||||
|
static const bool is_numeric = std::is_arithmetic<type>::value;
|
||||||
|
static const bool is_checked = false;
|
||||||
|
static const bool is_clamped = false;
|
||||||
|
static const bool is_strict = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct UnderlyingType<CheckedNumeric<T>> {
|
||||||
|
using type = T;
|
||||||
|
static const bool is_numeric = true;
|
||||||
|
static const bool is_checked = true;
|
||||||
|
static const bool is_clamped = false;
|
||||||
|
static const bool is_strict = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct UnderlyingType<ClampedNumeric<T>> {
|
||||||
|
using type = T;
|
||||||
|
static const bool is_numeric = true;
|
||||||
|
static const bool is_checked = false;
|
||||||
|
static const bool is_clamped = true;
|
||||||
|
static const bool is_strict = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct UnderlyingType<StrictNumeric<T>> {
|
||||||
|
using type = T;
|
||||||
|
static const bool is_numeric = true;
|
||||||
|
static const bool is_checked = false;
|
||||||
|
static const bool is_clamped = false;
|
||||||
|
static const bool is_strict = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename L, typename R>
|
||||||
|
struct IsCheckedOp {
|
||||||
|
static const bool value =
|
||||||
|
UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric &&
|
||||||
|
(UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked);
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename L, typename R>
|
||||||
|
struct IsClampedOp {
|
||||||
|
static const bool value =
|
||||||
|
UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric &&
|
||||||
|
(UnderlyingType<L>::is_clamped || UnderlyingType<R>::is_clamped) &&
|
||||||
|
!(UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked);
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename L, typename R>
|
||||||
|
struct IsStrictOp {
|
||||||
|
static const bool value =
|
||||||
|
UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric &&
|
||||||
|
(UnderlyingType<L>::is_strict || UnderlyingType<R>::is_strict) &&
|
||||||
|
!(UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked) &&
|
||||||
|
!(UnderlyingType<L>::is_clamped || UnderlyingType<R>::is_clamped);
|
||||||
|
};
|
||||||
|
|
||||||
|
// as_signed<> returns the supplied integral value (or integral castable
|
||||||
|
// Numeric template) cast as a signed integral of equivalent precision.
|
||||||
|
// I.e. it's mostly an alias for: static_cast<std::make_signed<T>::type>(t)
|
||||||
|
template <typename Src>
|
||||||
|
constexpr typename std::make_signed<
|
||||||
|
typename base::internal::UnderlyingType<Src>::type>::type
|
||||||
|
as_signed(const Src value) {
|
||||||
|
static_assert(std::is_integral<decltype(as_signed(value))>::value,
|
||||||
|
"Argument must be a signed or unsigned integer type.");
|
||||||
|
return static_cast<decltype(as_signed(value))>(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// as_unsigned<> returns the supplied integral value (or integral castable
|
||||||
|
// Numeric template) cast as an unsigned integral of equivalent precision.
|
||||||
|
// I.e. it's mostly an alias for: static_cast<std::make_unsigned<T>::type>(t)
|
||||||
|
template <typename Src>
|
||||||
|
constexpr typename std::make_unsigned<
|
||||||
|
typename base::internal::UnderlyingType<Src>::type>::type
|
||||||
|
as_unsigned(const Src value) {
|
||||||
|
static_assert(std::is_integral<decltype(as_unsigned(value))>::value,
|
||||||
|
"Argument must be a signed or unsigned integer type.");
|
||||||
|
return static_cast<decltype(as_unsigned(value))>(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename L, typename R>
|
||||||
|
constexpr bool IsLessImpl(const L lhs,
|
||||||
|
const R rhs,
|
||||||
|
const RangeCheck l_range,
|
||||||
|
const RangeCheck r_range) {
|
||||||
|
return l_range.IsUnderflow() || r_range.IsOverflow() ||
|
||||||
|
(l_range == r_range &&
|
||||||
|
static_cast<decltype(lhs + rhs)>(lhs) <
|
||||||
|
static_cast<decltype(lhs + rhs)>(rhs));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename L, typename R>
|
||||||
|
struct IsLess {
|
||||||
|
static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
|
||||||
|
"Types must be numeric.");
|
||||||
|
static constexpr bool Test(const L lhs, const R rhs) {
|
||||||
|
return IsLessImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs),
|
||||||
|
DstRangeRelationToSrcRange<L>(rhs));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename L, typename R>
|
||||||
|
constexpr bool IsLessOrEqualImpl(const L lhs,
|
||||||
|
const R rhs,
|
||||||
|
const RangeCheck l_range,
|
||||||
|
const RangeCheck r_range) {
|
||||||
|
return l_range.IsUnderflow() || r_range.IsOverflow() ||
|
||||||
|
(l_range == r_range &&
|
||||||
|
static_cast<decltype(lhs + rhs)>(lhs) <=
|
||||||
|
static_cast<decltype(lhs + rhs)>(rhs));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename L, typename R>
|
||||||
|
struct IsLessOrEqual {
|
||||||
|
static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
|
||||||
|
"Types must be numeric.");
|
||||||
|
static constexpr bool Test(const L lhs, const R rhs) {
|
||||||
|
return IsLessOrEqualImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs),
|
||||||
|
DstRangeRelationToSrcRange<L>(rhs));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename L, typename R>
|
||||||
|
constexpr bool IsGreaterImpl(const L lhs,
|
||||||
|
const R rhs,
|
||||||
|
const RangeCheck l_range,
|
||||||
|
const RangeCheck r_range) {
|
||||||
|
return l_range.IsOverflow() || r_range.IsUnderflow() ||
|
||||||
|
(l_range == r_range &&
|
||||||
|
static_cast<decltype(lhs + rhs)>(lhs) >
|
||||||
|
static_cast<decltype(lhs + rhs)>(rhs));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename L, typename R>
|
||||||
|
struct IsGreater {
|
||||||
|
static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
|
||||||
|
"Types must be numeric.");
|
||||||
|
static constexpr bool Test(const L lhs, const R rhs) {
|
||||||
|
return IsGreaterImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs),
|
||||||
|
DstRangeRelationToSrcRange<L>(rhs));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename L, typename R>
|
||||||
|
constexpr bool IsGreaterOrEqualImpl(const L lhs,
|
||||||
|
const R rhs,
|
||||||
|
const RangeCheck l_range,
|
||||||
|
const RangeCheck r_range) {
|
||||||
|
return l_range.IsOverflow() || r_range.IsUnderflow() ||
|
||||||
|
(l_range == r_range &&
|
||||||
|
static_cast<decltype(lhs + rhs)>(lhs) >=
|
||||||
|
static_cast<decltype(lhs + rhs)>(rhs));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename L, typename R>
|
||||||
|
struct IsGreaterOrEqual {
|
||||||
|
static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
|
||||||
|
"Types must be numeric.");
|
||||||
|
static constexpr bool Test(const L lhs, const R rhs) {
|
||||||
|
return IsGreaterOrEqualImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs),
|
||||||
|
DstRangeRelationToSrcRange<L>(rhs));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename L, typename R>
|
||||||
|
struct IsEqual {
|
||||||
|
static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
|
||||||
|
"Types must be numeric.");
|
||||||
|
static constexpr bool Test(const L lhs, const R rhs) {
|
||||||
|
return DstRangeRelationToSrcRange<R>(lhs) ==
|
||||||
|
DstRangeRelationToSrcRange<L>(rhs) &&
|
||||||
|
static_cast<decltype(lhs + rhs)>(lhs) ==
|
||||||
|
static_cast<decltype(lhs + rhs)>(rhs);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename L, typename R>
|
||||||
|
struct IsNotEqual {
|
||||||
|
static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
|
||||||
|
"Types must be numeric.");
|
||||||
|
static constexpr bool Test(const L lhs, const R rhs) {
|
||||||
|
return DstRangeRelationToSrcRange<R>(lhs) !=
|
||||||
|
DstRangeRelationToSrcRange<L>(rhs) ||
|
||||||
|
static_cast<decltype(lhs + rhs)>(lhs) !=
|
||||||
|
static_cast<decltype(lhs + rhs)>(rhs);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// These perform the actual math operations on the CheckedNumerics.
|
||||||
|
// Binary arithmetic operations.
|
||||||
|
template <template <typename, typename> class C, typename L, typename R>
|
||||||
|
constexpr bool SafeCompare(const L lhs, const R rhs) {
|
||||||
|
static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
|
||||||
|
"Types must be numeric.");
|
||||||
|
using Promotion = BigEnoughPromotion<L, R>;
|
||||||
|
using BigType = typename Promotion::type;
|
||||||
|
return Promotion::is_contained
|
||||||
|
// Force to a larger type for speed if both are contained.
|
||||||
|
? C<BigType, BigType>::Test(
|
||||||
|
static_cast<BigType>(static_cast<L>(lhs)),
|
||||||
|
static_cast<BigType>(static_cast<R>(rhs)))
|
||||||
|
// Let the template functions figure it out for mixed types.
|
||||||
|
: C<L, R>::Test(lhs, rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Dst, typename Src>
|
||||||
|
constexpr bool IsMaxInRangeForNumericType() {
|
||||||
|
return IsGreaterOrEqual<Dst, Src>::Test(std::numeric_limits<Dst>::max(),
|
||||||
|
std::numeric_limits<Src>::max());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Dst, typename Src>
|
||||||
|
constexpr bool IsMinInRangeForNumericType() {
|
||||||
|
return IsLessOrEqual<Dst, Src>::Test(std::numeric_limits<Dst>::lowest(),
|
||||||
|
std::numeric_limits<Src>::lowest());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Dst, typename Src>
|
||||||
|
constexpr Dst CommonMax() {
|
||||||
|
return !IsMaxInRangeForNumericType<Dst, Src>()
|
||||||
|
? Dst(std::numeric_limits<Dst>::max())
|
||||||
|
: Dst(std::numeric_limits<Src>::max());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Dst, typename Src>
|
||||||
|
constexpr Dst CommonMin() {
|
||||||
|
return !IsMinInRangeForNumericType<Dst, Src>()
|
||||||
|
? Dst(std::numeric_limits<Dst>::lowest())
|
||||||
|
: Dst(std::numeric_limits<Src>::lowest());
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is a wrapper to generate return the max or min for a supplied type.
|
||||||
|
// If the argument is false, the returned value is the maximum. If true the
|
||||||
|
// returned value is the minimum.
|
||||||
|
template <typename Dst, typename Src = Dst>
|
||||||
|
constexpr Dst CommonMaxOrMin(bool is_min) {
|
||||||
|
return is_min ? CommonMin<Dst, Src>() : CommonMax<Dst, Src>();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace base
|
||||||
|
|
||||||
|
#endif // BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_
|
|
@ -0,0 +1,12 @@
|
||||||
|
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#ifndef BASE_NUMERICS_SAFE_MATH_H_
|
||||||
|
#define BASE_NUMERICS_SAFE_MATH_H_
|
||||||
|
|
||||||
|
#include "base/numerics/checked_math.h"
|
||||||
|
#include "base/numerics/clamped_math.h"
|
||||||
|
#include "base/numerics/safe_conversions.h"
|
||||||
|
|
||||||
|
#endif // BASE_NUMERICS_SAFE_MATH_H_
|
|
@ -0,0 +1,122 @@
|
||||||
|
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#ifndef BASE_NUMERICS_SAFE_MATH_ARM_IMPL_H_
|
||||||
|
#define BASE_NUMERICS_SAFE_MATH_ARM_IMPL_H_
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <limits>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
#include "base/numerics/safe_conversions.h"
|
||||||
|
|
||||||
|
namespace base {
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
template <typename T, typename U>
|
||||||
|
struct CheckedMulFastAsmOp {
|
||||||
|
static const bool is_supported =
|
||||||
|
FastIntegerArithmeticPromotion<T, U>::is_contained;
|
||||||
|
|
||||||
|
// The following is much more efficient than the Clang and GCC builtins for
|
||||||
|
// performing overflow-checked multiplication when a twice wider type is
|
||||||
|
// available. The below compiles down to 2-3 instructions, depending on the
|
||||||
|
// width of the types in use.
|
||||||
|
// As an example, an int32_t multiply compiles to:
|
||||||
|
// smull r0, r1, r0, r1
|
||||||
|
// cmp r1, r1, asr #31
|
||||||
|
// And an int16_t multiply compiles to:
|
||||||
|
// smulbb r1, r1, r0
|
||||||
|
// asr r2, r1, #16
|
||||||
|
// cmp r2, r1, asr #15
|
||||||
|
template <typename V>
|
||||||
|
__attribute__((always_inline)) static bool Do(T x, U y, V* result) {
|
||||||
|
using Promotion = typename FastIntegerArithmeticPromotion<T, U>::type;
|
||||||
|
Promotion presult;
|
||||||
|
|
||||||
|
presult = static_cast<Promotion>(x) * static_cast<Promotion>(y);
|
||||||
|
*result = static_cast<V>(presult);
|
||||||
|
return IsValueInRangeForNumericType<V>(presult);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename U>
|
||||||
|
struct ClampedAddFastAsmOp {
|
||||||
|
static const bool is_supported =
|
||||||
|
BigEnoughPromotion<T, U>::is_contained &&
|
||||||
|
IsTypeInRangeForNumericType<
|
||||||
|
int32_t,
|
||||||
|
typename BigEnoughPromotion<T, U>::type>::value;
|
||||||
|
|
||||||
|
template <typename V>
|
||||||
|
__attribute__((always_inline)) static V Do(T x, U y) {
|
||||||
|
// This will get promoted to an int, so let the compiler do whatever is
|
||||||
|
// clever and rely on the saturated cast to bounds check.
|
||||||
|
if (IsIntegerArithmeticSafe<int, T, U>::value)
|
||||||
|
return saturated_cast<V>(x + y);
|
||||||
|
|
||||||
|
int32_t result;
|
||||||
|
int32_t x_i32 = checked_cast<int32_t>(x);
|
||||||
|
int32_t y_i32 = checked_cast<int32_t>(y);
|
||||||
|
|
||||||
|
asm("qadd %[result], %[first], %[second]"
|
||||||
|
: [result] "=r"(result)
|
||||||
|
: [first] "r"(x_i32), [second] "r"(y_i32));
|
||||||
|
return saturated_cast<V>(result);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename U>
|
||||||
|
struct ClampedSubFastAsmOp {
|
||||||
|
static const bool is_supported =
|
||||||
|
BigEnoughPromotion<T, U>::is_contained &&
|
||||||
|
IsTypeInRangeForNumericType<
|
||||||
|
int32_t,
|
||||||
|
typename BigEnoughPromotion<T, U>::type>::value;
|
||||||
|
|
||||||
|
template <typename V>
|
||||||
|
__attribute__((always_inline)) static V Do(T x, U y) {
|
||||||
|
// This will get promoted to an int, so let the compiler do whatever is
|
||||||
|
// clever and rely on the saturated cast to bounds check.
|
||||||
|
if (IsIntegerArithmeticSafe<int, T, U>::value)
|
||||||
|
return saturated_cast<V>(x - y);
|
||||||
|
|
||||||
|
int32_t result;
|
||||||
|
int32_t x_i32 = checked_cast<int32_t>(x);
|
||||||
|
int32_t y_i32 = checked_cast<int32_t>(y);
|
||||||
|
|
||||||
|
asm("qsub %[result], %[first], %[second]"
|
||||||
|
: [result] "=r"(result)
|
||||||
|
: [first] "r"(x_i32), [second] "r"(y_i32));
|
||||||
|
return saturated_cast<V>(result);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename U>
|
||||||
|
struct ClampedMulFastAsmOp {
|
||||||
|
static const bool is_supported = CheckedMulFastAsmOp<T, U>::is_supported;
|
||||||
|
|
||||||
|
template <typename V>
|
||||||
|
__attribute__((always_inline)) static V Do(T x, U y) {
|
||||||
|
// Use the CheckedMulFastAsmOp for full-width 32-bit values, because
|
||||||
|
// it's fewer instructions than promoting and then saturating.
|
||||||
|
if (!IsIntegerArithmeticSafe<int32_t, T, U>::value &&
|
||||||
|
!IsIntegerArithmeticSafe<uint32_t, T, U>::value) {
|
||||||
|
V result;
|
||||||
|
if (CheckedMulFastAsmOp<T, U>::Do(x, y, &result))
|
||||||
|
return result;
|
||||||
|
return CommonMaxOrMin<V>(IsValueNegative(x) ^ IsValueNegative(y));
|
||||||
|
}
|
||||||
|
|
||||||
|
assert((FastIntegerArithmeticPromotion<T, U>::is_contained));
|
||||||
|
using Promotion = typename FastIntegerArithmeticPromotion<T, U>::type;
|
||||||
|
return saturated_cast<V>(static_cast<Promotion>(x) *
|
||||||
|
static_cast<Promotion>(y));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace base
|
||||||
|
|
||||||
|
#endif // BASE_NUMERICS_SAFE_MATH_ARM_IMPL_H_
|
|
@ -0,0 +1,157 @@
|
||||||
|
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#ifndef BASE_NUMERICS_SAFE_MATH_CLANG_GCC_IMPL_H_
|
||||||
|
#define BASE_NUMERICS_SAFE_MATH_CLANG_GCC_IMPL_H_
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <limits>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
#include "base/numerics/safe_conversions.h"
|
||||||
|
|
||||||
|
#if !defined(__native_client__) && (defined(__ARMEL__) || defined(__arch64__))
|
||||||
|
#include "base/numerics/safe_math_arm_impl.h"
|
||||||
|
#define BASE_HAS_ASSEMBLER_SAFE_MATH (1)
|
||||||
|
#else
|
||||||
|
#define BASE_HAS_ASSEMBLER_SAFE_MATH (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace base {
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
// These are the non-functioning boilerplate implementations of the optimized
|
||||||
|
// safe math routines.
|
||||||
|
#if !BASE_HAS_ASSEMBLER_SAFE_MATH
|
||||||
|
template <typename T, typename U>
|
||||||
|
struct CheckedMulFastAsmOp {
|
||||||
|
static const bool is_supported = false;
|
||||||
|
template <typename V>
|
||||||
|
static constexpr bool Do(T, U, V*) {
|
||||||
|
// Force a compile failure if instantiated.
|
||||||
|
return CheckOnFailure::template HandleFailure<bool>();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename U>
|
||||||
|
struct ClampedAddFastAsmOp {
|
||||||
|
static const bool is_supported = false;
|
||||||
|
template <typename V>
|
||||||
|
static constexpr V Do(T, U) {
|
||||||
|
// Force a compile failure if instantiated.
|
||||||
|
return CheckOnFailure::template HandleFailure<V>();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename U>
|
||||||
|
struct ClampedSubFastAsmOp {
|
||||||
|
static const bool is_supported = false;
|
||||||
|
template <typename V>
|
||||||
|
static constexpr V Do(T, U) {
|
||||||
|
// Force a compile failure if instantiated.
|
||||||
|
return CheckOnFailure::template HandleFailure<V>();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename U>
|
||||||
|
struct ClampedMulFastAsmOp {
|
||||||
|
static const bool is_supported = false;
|
||||||
|
template <typename V>
|
||||||
|
static constexpr V Do(T, U) {
|
||||||
|
// Force a compile failure if instantiated.
|
||||||
|
return CheckOnFailure::template HandleFailure<V>();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#endif // BASE_HAS_ASSEMBLER_SAFE_MATH
|
||||||
|
#undef BASE_HAS_ASSEMBLER_SAFE_MATH
|
||||||
|
|
||||||
|
template <typename T, typename U>
|
||||||
|
struct CheckedAddFastOp {
|
||||||
|
static const bool is_supported = true;
|
||||||
|
template <typename V>
|
||||||
|
__attribute__((always_inline)) static constexpr bool Do(T x, U y, V* result) {
|
||||||
|
return !__builtin_add_overflow(x, y, result);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename U>
|
||||||
|
struct CheckedSubFastOp {
|
||||||
|
static const bool is_supported = true;
|
||||||
|
template <typename V>
|
||||||
|
__attribute__((always_inline)) static constexpr bool Do(T x, U y, V* result) {
|
||||||
|
return !__builtin_sub_overflow(x, y, result);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename U>
|
||||||
|
struct CheckedMulFastOp {
|
||||||
|
#if defined(__clang__)
|
||||||
|
// TODO(jschuh): Get the Clang runtime library issues sorted out so we can
|
||||||
|
// support full-width, mixed-sign multiply builtins.
|
||||||
|
// https://crbug.com/613003
|
||||||
|
// We can support intptr_t, uintptr_t, or a smaller common type.
|
||||||
|
static const bool is_supported =
|
||||||
|
(IsTypeInRangeForNumericType<intptr_t, T>::value &&
|
||||||
|
IsTypeInRangeForNumericType<intptr_t, U>::value) ||
|
||||||
|
(IsTypeInRangeForNumericType<uintptr_t, T>::value &&
|
||||||
|
IsTypeInRangeForNumericType<uintptr_t, U>::value);
|
||||||
|
#else
|
||||||
|
static const bool is_supported = true;
|
||||||
|
#endif
|
||||||
|
template <typename V>
|
||||||
|
__attribute__((always_inline)) static constexpr bool Do(T x, U y, V* result) {
|
||||||
|
return CheckedMulFastAsmOp<T, U>::is_supported
|
||||||
|
? CheckedMulFastAsmOp<T, U>::Do(x, y, result)
|
||||||
|
: !__builtin_mul_overflow(x, y, result);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename U>
|
||||||
|
struct ClampedAddFastOp {
|
||||||
|
static const bool is_supported = ClampedAddFastAsmOp<T, U>::is_supported;
|
||||||
|
template <typename V>
|
||||||
|
__attribute__((always_inline)) static V Do(T x, U y) {
|
||||||
|
return ClampedAddFastAsmOp<T, U>::template Do<V>(x, y);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename U>
|
||||||
|
struct ClampedSubFastOp {
|
||||||
|
static const bool is_supported = ClampedSubFastAsmOp<T, U>::is_supported;
|
||||||
|
template <typename V>
|
||||||
|
__attribute__((always_inline)) static V Do(T x, U y) {
|
||||||
|
return ClampedSubFastAsmOp<T, U>::template Do<V>(x, y);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename U>
|
||||||
|
struct ClampedMulFastOp {
|
||||||
|
static const bool is_supported = ClampedMulFastAsmOp<T, U>::is_supported;
|
||||||
|
template <typename V>
|
||||||
|
__attribute__((always_inline)) static V Do(T x, U y) {
|
||||||
|
return ClampedMulFastAsmOp<T, U>::template Do<V>(x, y);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct ClampedNegFastOp {
|
||||||
|
static const bool is_supported = std::is_signed<T>::value;
|
||||||
|
__attribute__((always_inline)) static T Do(T value) {
|
||||||
|
// Use this when there is no assembler path available.
|
||||||
|
if (!ClampedSubFastAsmOp<T, T>::is_supported) {
|
||||||
|
T result;
|
||||||
|
return !__builtin_sub_overflow(T(0), value, &result)
|
||||||
|
? result
|
||||||
|
: std::numeric_limits<T>::max();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to the normal subtraction path.
|
||||||
|
return ClampedSubFastOp<T, T>::template Do<T>(T(0), value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace base
|
||||||
|
|
||||||
|
#endif // BASE_NUMERICS_SAFE_MATH_CLANG_GCC_IMPL_H_
|
|
@ -0,0 +1,240 @@
|
||||||
|
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#ifndef BASE_NUMERICS_SAFE_MATH_SHARED_IMPL_H_
|
||||||
|
#define BASE_NUMERICS_SAFE_MATH_SHARED_IMPL_H_
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <climits>
|
||||||
|
#include <cmath>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <limits>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
#include "base/numerics/safe_conversions.h"
|
||||||
|
|
||||||
|
#ifdef __asmjs__
|
||||||
|
// Optimized safe math instructions are incompatible with asmjs.
|
||||||
|
#define BASE_HAS_OPTIMIZED_SAFE_MATH (0)
|
||||||
|
// Where available use builtin math overflow support on Clang and GCC.
|
||||||
|
#elif !defined(__native_client__) && \
|
||||||
|
((defined(__clang__) && \
|
||||||
|
((__clang_major__ > 3) || \
|
||||||
|
(__clang_major__ == 3 && __clang_minor__ >= 4))) || \
|
||||||
|
(defined(__GNUC__) && __GNUC__ >= 5))
|
||||||
|
#include "base/numerics/safe_math_clang_gcc_impl.h"
|
||||||
|
#define BASE_HAS_OPTIMIZED_SAFE_MATH (1)
|
||||||
|
#else
|
||||||
|
#define BASE_HAS_OPTIMIZED_SAFE_MATH (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace base {
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
// These are the non-functioning boilerplate implementations of the optimized
|
||||||
|
// safe math routines.
|
||||||
|
#if !BASE_HAS_OPTIMIZED_SAFE_MATH
|
||||||
|
template <typename T, typename U>
|
||||||
|
struct CheckedAddFastOp {
|
||||||
|
static const bool is_supported = false;
|
||||||
|
template <typename V>
|
||||||
|
static constexpr bool Do(T, U, V*) {
|
||||||
|
// Force a compile failure if instantiated.
|
||||||
|
return CheckOnFailure::template HandleFailure<bool>();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename U>
|
||||||
|
struct CheckedSubFastOp {
|
||||||
|
static const bool is_supported = false;
|
||||||
|
template <typename V>
|
||||||
|
static constexpr bool Do(T, U, V*) {
|
||||||
|
// Force a compile failure if instantiated.
|
||||||
|
return CheckOnFailure::template HandleFailure<bool>();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename U>
|
||||||
|
struct CheckedMulFastOp {
|
||||||
|
static const bool is_supported = false;
|
||||||
|
template <typename V>
|
||||||
|
static constexpr bool Do(T, U, V*) {
|
||||||
|
// Force a compile failure if instantiated.
|
||||||
|
return CheckOnFailure::template HandleFailure<bool>();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename U>
|
||||||
|
struct ClampedAddFastOp {
|
||||||
|
static const bool is_supported = false;
|
||||||
|
template <typename V>
|
||||||
|
static constexpr V Do(T, U) {
|
||||||
|
// Force a compile failure if instantiated.
|
||||||
|
return CheckOnFailure::template HandleFailure<V>();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename U>
|
||||||
|
struct ClampedSubFastOp {
|
||||||
|
static const bool is_supported = false;
|
||||||
|
template <typename V>
|
||||||
|
static constexpr V Do(T, U) {
|
||||||
|
// Force a compile failure if instantiated.
|
||||||
|
return CheckOnFailure::template HandleFailure<V>();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename U>
|
||||||
|
struct ClampedMulFastOp {
|
||||||
|
static const bool is_supported = false;
|
||||||
|
template <typename V>
|
||||||
|
static constexpr V Do(T, U) {
|
||||||
|
// Force a compile failure if instantiated.
|
||||||
|
return CheckOnFailure::template HandleFailure<V>();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct ClampedNegFastOp {
|
||||||
|
static const bool is_supported = false;
|
||||||
|
static constexpr T Do(T) {
|
||||||
|
// Force a compile failure if instantiated.
|
||||||
|
return CheckOnFailure::template HandleFailure<T>();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#endif // BASE_HAS_OPTIMIZED_SAFE_MATH
|
||||||
|
#undef BASE_HAS_OPTIMIZED_SAFE_MATH
|
||||||
|
|
||||||
|
// This is used for UnsignedAbs, where we need to support floating-point
|
||||||
|
// template instantiations even though we don't actually support the operations.
|
||||||
|
// However, there is no corresponding implementation of e.g. SafeUnsignedAbs,
|
||||||
|
// so the float versions will not compile.
|
||||||
|
template <typename Numeric,
|
||||||
|
bool IsInteger = std::is_integral<Numeric>::value,
|
||||||
|
bool IsFloat = std::is_floating_point<Numeric>::value>
|
||||||
|
struct UnsignedOrFloatForSize;
|
||||||
|
|
||||||
|
template <typename Numeric>
|
||||||
|
struct UnsignedOrFloatForSize<Numeric, true, false> {
|
||||||
|
using type = typename std::make_unsigned<Numeric>::type;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Numeric>
|
||||||
|
struct UnsignedOrFloatForSize<Numeric, false, true> {
|
||||||
|
using type = Numeric;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Wrap the unary operations to allow SFINAE when instantiating integrals versus
|
||||||
|
// floating points. These don't perform any overflow checking. Rather, they
|
||||||
|
// exhibit well-defined overflow semantics and rely on the caller to detect
|
||||||
|
// if an overflow occured.
|
||||||
|
|
||||||
|
template <typename T,
|
||||||
|
typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
|
||||||
|
constexpr T NegateWrapper(T value) {
|
||||||
|
using UnsignedT = typename std::make_unsigned<T>::type;
|
||||||
|
// This will compile to a NEG on Intel, and is normal negation on ARM.
|
||||||
|
return static_cast<T>(UnsignedT(0) - static_cast<UnsignedT>(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <
|
||||||
|
typename T,
|
||||||
|
typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
|
||||||
|
constexpr T NegateWrapper(T value) {
|
||||||
|
return -value;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T,
|
||||||
|
typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
|
||||||
|
constexpr typename std::make_unsigned<T>::type InvertWrapper(T value) {
|
||||||
|
return ~value;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T,
|
||||||
|
typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
|
||||||
|
constexpr T AbsWrapper(T value) {
|
||||||
|
return static_cast<T>(SafeUnsignedAbs(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <
|
||||||
|
typename T,
|
||||||
|
typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
|
||||||
|
constexpr T AbsWrapper(T value) {
|
||||||
|
return value < 0 ? -value : value;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <template <typename, typename, typename> class M,
|
||||||
|
typename L,
|
||||||
|
typename R>
|
||||||
|
struct MathWrapper {
|
||||||
|
using math = M<typename UnderlyingType<L>::type,
|
||||||
|
typename UnderlyingType<R>::type,
|
||||||
|
void>;
|
||||||
|
using type = typename math::result_type;
|
||||||
|
};
|
||||||
|
|
||||||
|
// These variadic templates work out the return types.
|
||||||
|
// TODO(jschuh): Rip all this out once we have C++14 non-trailing auto support.
|
||||||
|
template <template <typename, typename, typename> class M,
|
||||||
|
typename L,
|
||||||
|
typename R,
|
||||||
|
typename... Args>
|
||||||
|
struct ResultType;
|
||||||
|
|
||||||
|
template <template <typename, typename, typename> class M,
|
||||||
|
typename L,
|
||||||
|
typename R>
|
||||||
|
struct ResultType<M, L, R> {
|
||||||
|
using type = typename MathWrapper<M, L, R>::type;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <template <typename, typename, typename> class M,
|
||||||
|
typename L,
|
||||||
|
typename R,
|
||||||
|
typename... Args>
|
||||||
|
struct ResultType {
|
||||||
|
using type =
|
||||||
|
typename ResultType<M, typename ResultType<M, L, R>::type, Args...>::type;
|
||||||
|
};
|
||||||
|
|
||||||
|
// The following macros are just boilerplate for the standard arithmetic
|
||||||
|
// operator overloads and variadic function templates. A macro isn't the nicest
|
||||||
|
// solution, but it beats rewriting these over and over again.
|
||||||
|
#define BASE_NUMERIC_ARITHMETIC_VARIADIC(CLASS, CL_ABBR, OP_NAME) \
|
||||||
|
template <typename L, typename R, typename... Args> \
|
||||||
|
constexpr CLASS##Numeric< \
|
||||||
|
typename ResultType<CLASS##OP_NAME##Op, L, R, Args...>::type> \
|
||||||
|
CL_ABBR##OP_NAME(const L lhs, const R rhs, const Args... args) { \
|
||||||
|
return CL_ABBR##MathOp<CLASS##OP_NAME##Op, L, R, Args...>(lhs, rhs, \
|
||||||
|
args...); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define BASE_NUMERIC_ARITHMETIC_OPERATORS(CLASS, CL_ABBR, OP_NAME, OP, CMP_OP) \
|
||||||
|
/* Binary arithmetic operator for all CLASS##Numeric operations. */ \
|
||||||
|
template <typename L, typename R, \
|
||||||
|
typename std::enable_if<Is##CLASS##Op<L, R>::value>::type* = \
|
||||||
|
nullptr> \
|
||||||
|
constexpr CLASS##Numeric< \
|
||||||
|
typename MathWrapper<CLASS##OP_NAME##Op, L, R>::type> \
|
||||||
|
operator OP(const L lhs, const R rhs) { \
|
||||||
|
return decltype(lhs OP rhs)::template MathOp<CLASS##OP_NAME##Op>(lhs, \
|
||||||
|
rhs); \
|
||||||
|
} \
|
||||||
|
/* Assignment arithmetic operator implementation from CLASS##Numeric. */ \
|
||||||
|
template <typename L> \
|
||||||
|
template <typename R> \
|
||||||
|
constexpr CLASS##Numeric<L>& CLASS##Numeric<L>::operator CMP_OP( \
|
||||||
|
const R rhs) { \
|
||||||
|
return MathOp<CLASS##OP_NAME##Op>(rhs); \
|
||||||
|
} \
|
||||||
|
/* Variadic arithmetic functions that return CLASS##Numeric. */ \
|
||||||
|
BASE_NUMERIC_ARITHMETIC_VARIADIC(CLASS, CL_ABBR, OP_NAME)
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace base
|
||||||
|
|
||||||
|
#endif // BASE_NUMERICS_SAFE_MATH_SHARED_IMPL_H_
|
|
@ -0,0 +1,13 @@
|
||||||
|
{"Registrations":[
|
||||||
|
{
|
||||||
|
"component": {
|
||||||
|
"type": "git",
|
||||||
|
"git": {
|
||||||
|
"repositoryUrl": "https://github.com/chromium/chromium",
|
||||||
|
"commitHash": "d8710dd959da8e3be56f20af8cc94fbf560fbb6b"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Version": 1
|
||||||
|
}
|
|
@ -86,7 +86,7 @@
|
||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
<ItemDefinitionGroup>
|
<ItemDefinitionGroup>
|
||||||
<ClCompile>
|
<ClCompile>
|
||||||
<AdditionalIncludeDirectories>$(OpenConsoleDir)\src\inc;$(OpenConsoleDir)\dep;$(OpenConsoleDir)\dep\Console;$(OpenConsoleDir)\dep\Win32K;$(OpenConsoleDir)\dep\gsl\include;$(OpenConsoleDir)\dep\wil\include;%(AdditionalIncludeDirectories);</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>$(OpenConsoleDir)\src\inc;$(OpenConsoleDir)\dep;$(OpenConsoleDir)\dep\Console;$(OpenConsoleDir)\dep\chromium;$(OpenConsoleDir)\dep\Win32K;$(OpenConsoleDir)\dep\gsl\include;$(OpenConsoleDir)\dep\wil\include;%(AdditionalIncludeDirectories);</AdditionalIncludeDirectories>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile>
|
<ClCompile>
|
||||||
<!-- Manually include the generated TerminalCore header's path, because
|
<!-- Manually include the generated TerminalCore header's path, because
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#define NOMINMAX
|
||||||
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <winrt/Windows.Foundation.h>
|
#include <winrt/Windows.Foundation.h>
|
||||||
#include <winrt/Windows.Foundation.Collections.h>
|
#include <winrt/Windows.Foundation.Collections.h>
|
||||||
|
|
|
@ -82,7 +82,7 @@
|
||||||
<PrecompiledHeaderFile>precomp.h</PrecompiledHeaderFile>
|
<PrecompiledHeaderFile>precomp.h</PrecompiledHeaderFile>
|
||||||
<ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
|
<ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
|
||||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||||
<AdditionalIncludeDirectories>$(SolutionDir)\src\inc;$(SolutionDir)\dep;$(SolutionDir)\dep\Console;$(SolutionDir)\dep\Win32K;$(SolutionDir)\dep\gsl\include;$(SolutionDir)\dep\wil\include;%(AdditionalIncludeDirectories);</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>$(SolutionDir)\src\inc;$(SolutionDir)\dep;$(SolutionDir)\dep\Console;$(SolutionDir)\dep\chromium;$(SolutionDir)\dep\Win32K;$(SolutionDir)\dep\gsl\include;$(SolutionDir)\dep\wil\include;%(AdditionalIncludeDirectories);</AdditionalIncludeDirectories>
|
||||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||||
<MinimalRebuild>false</MinimalRebuild>
|
<MinimalRebuild>false</MinimalRebuild>
|
||||||
<RuntimeTypeInfo>false</RuntimeTypeInfo>
|
<RuntimeTypeInfo>false</RuntimeTypeInfo>
|
||||||
|
|
|
@ -68,6 +68,9 @@
|
||||||
// CppCoreCheck
|
// CppCoreCheck
|
||||||
#include <CppCoreCheck/Warnings.h>
|
#include <CppCoreCheck/Warnings.h>
|
||||||
|
|
||||||
|
// Chromium Numerics (safe math)
|
||||||
|
#include <base/numerics/safe_math.h>
|
||||||
|
|
||||||
// IntSafe
|
// IntSafe
|
||||||
#define ENABLE_INTSAFE_SIGNED_FUNCTIONS
|
#define ENABLE_INTSAFE_SIGNED_FUNCTIONS
|
||||||
#include <intsafe.h>
|
#include <intsafe.h>
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
#define DEFINE_CONSOLEV2_PROPERTIES
|
#define DEFINE_CONSOLEV2_PROPERTIES
|
||||||
#define INC_OLE2
|
#define INC_OLE2
|
||||||
|
|
||||||
|
#define NOMINMAX
|
||||||
|
|
||||||
#define WIN32_NO_STATUS
|
#define WIN32_NO_STATUS
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#undef WIN32_NO_STATUS
|
#undef WIN32_NO_STATUS
|
||||||
|
|
|
@ -21,6 +21,8 @@ Abstract:
|
||||||
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
|
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define NOMINMAX
|
||||||
|
|
||||||
// Windows Header Files:
|
// Windows Header Files:
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#define NOMINMAX
|
||||||
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <shlwapi.h>
|
#include <shlwapi.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
|
@ -14,6 +14,8 @@ Abstract:
|
||||||
#define _CRT_SECURE_NO_WARNINGS 1
|
#define _CRT_SECURE_NO_WARNINGS 1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define NOMINMAX
|
||||||
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
|
@ -19,6 +19,8 @@ Notes:
|
||||||
|
|
||||||
--*/
|
--*/
|
||||||
|
|
||||||
|
#define NOMINMAX
|
||||||
|
|
||||||
#define _OLEAUT32_
|
#define _OLEAUT32_
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <ole2.h>
|
#include <ole2.h>
|
||||||
|
|
Загрузка…
Ссылка в новой задаче