Skip to content

Commit

Permalink
change the checked_multiply function to not use undefined behavior to…
Browse files Browse the repository at this point in the history
… check for potential undefined behavior and wrapping.
  • Loading branch information
phlptp committed Jul 26, 2019
1 parent dbd4933 commit c8da413
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 5 deletions.
18 changes: 13 additions & 5 deletions include/CLI/Validators.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#pragma once

// Distributed under the 3-Clause BSD License. See accompanying
// file LICENSE or https://github.com/CLIUtils/CLI11 for details.

Expand All @@ -9,6 +8,7 @@
#include <cmath>
#include <functional>
#include <iostream>
#include <limits>
#include <map>
#include <memory>
#include <string>
Expand Down Expand Up @@ -509,18 +509,26 @@ auto search(const T &set, const V &val, const std::function<V(V)> &filter_functi
});
return {(it != std::end(setref)), it};
}
/// Generate the absolute value of a number
template <typename T> inline typename std::enable_if<std::is_signed<T>::value, T>::type absval(T a) {
return static_cast<T>((std::abs)(a));
}
/// unsigned values just return the value
template <typename T> inline typename std::enable_if<!std::is_signed<T>::value, T>::type absval(T a) { return a; }

/// Performs a *= b; if it doesn't cause integer overflow. Returns false otherwise.
template <typename T> typename std::enable_if<std::is_integral<T>::value, bool>::type checked_multiply(T &a, T b) {
if(a == 0 || b == 0) {
if(a == 0 || b == 0 || a == 1 || b == 1) {
a *= b;
return true;
}
T c = a * b;
if(c / a != b) {
if(a == (std::numeric_limits<T>::min)() || b == (std::numeric_limits<T>::min)()) {
return false;
}
a = c;
if((std::numeric_limits<T>::max)() / absval(a) < absval(b)) {
return false;
}
a *= b;
return true;
}

Expand Down
10 changes: 10 additions & 0 deletions tests/HelpersTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -423,10 +423,20 @@ TEST(CheckedMultiply, Int) {
ASSERT_FALSE(CLI::detail::checked_multiply(a, b));
ASSERT_EQ(a, std::numeric_limits<int>::min());

b = std::numeric_limits<int>::min();
a = -1;
ASSERT_FALSE(CLI::detail::checked_multiply(a, b));
ASSERT_EQ(a, -1);

a = std::numeric_limits<int>::min() / 100;
b = 99;
ASSERT_TRUE(CLI::detail::checked_multiply(a, b));
ASSERT_EQ(a, std::numeric_limits<int>::min() / 100 * 99);

a = std::numeric_limits<int>::min() / 100;
b = -101;
ASSERT_FALSE(CLI::detail::checked_multiply(a, b));
ASSERT_EQ(a, std::numeric_limits<int>::min() / 100);
}

TEST(CheckedMultiply, SizeT) {
Expand Down

0 comments on commit c8da413

Please sign in to comment.