Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fuzz equation failure #51

Merged
merged 8 commits into from
Mar 30, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ Add a converter command line application and fix a few slow conversion issues an
- fixed a few initial issues from fuzz_measurement [#42][]
- Some more fuzzing generated issues with cascading powers [#45][]
- A number of additional clang-tidy checks were added and the resulting warnings fixed [#46][]
- An issue from the fuzzer dealing with equation type units [#51][]

### Added
- added a [converter](https://units.readthedocs.io/en/latest/introduction/converter.html) command line application that can convert units on the command line [#35][]
Expand Down Expand Up @@ -101,6 +102,7 @@ Continued work on cleaning up the library and starting to add main documentation
[#45]: /~https://github.com/LLNL/units/pull/45
[#46]: /~https://github.com/LLNL/units/pull/46
[#47]: /~https://github.com/LLNL/units/pull/47
[#51]: /~https://github.com/LLNL/units/pull/51

[0.4.0]: /~https://github.com/LLNL/units/releases/tag/v0.4.0
[0.3.0]: /~https://github.com/LLNL/units/releases/tag/v0.3.0
Expand Down
1 change: 1 addition & 0 deletions test/files/fuzz_issues/meas_fail17
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pupt.pupt.EQXUN[86]999999
1 change: 1 addition & 0 deletions test/files/fuzz_issues/meas_fail18
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pupt.pupt.EQXUN[41]999999
1 change: 1 addition & 0 deletions test/files/fuzz_issues/meas_fail19
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
�EQXUN[1]>3333333333333333333300000000000000000000000000000000000000000000000000000000000000006148914691236517204>EN[]>>metreu8>[�]^34s
1 change: 1 addition & 0 deletions test/files/fuzz_issues/meas_fail20
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
squacenti6-
1 change: 1 addition & 0 deletions test/files/fuzz_issues/meas_fail21
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/A/3����������������������������������A/3�������������������������������������������������
2 changes: 1 addition & 1 deletion test/fuzz_issue_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -369,4 +369,4 @@ TEST_P(measProblems, measFiles)
}
}

INSTANTIATE_TEST_SUITE_P(measFiles, measProblems, ::testing::Range(1, 17));
INSTANTIATE_TEST_SUITE_P(measFiles, measProblems, ::testing::Range(1, 22));
2 changes: 2 additions & 0 deletions test/test_unit_strings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -677,6 +677,8 @@ TEST(userDefinedUnits, definitions)
EXPECT_EQ(unit_from_string("clucks/A"), precise_unit(19.3, precise::m));

EXPECT_EQ(to_string(clucks), "clucks");

EXPECT_EQ(to_string(clucks.pow(2)), "clucks^2");
}

TEST(userDefinedUnits, definitionStrings)
Expand Down
148 changes: 103 additions & 45 deletions units/units.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -361,10 +361,19 @@ static const std::unordered_map<float, char> si_prefixes{
{1e12F, 'T'}, {1.0F / 1e-12F, 'T'}};

// check if the character is something that could begin a number
static inline bool isNumericalCharacter(char X)
static inline bool isNumericalStartCharacter(char X)
{
return ((X >= '0' && X <= '9') || X == '-' || X == '+' || X == '.');
}

// check if the character is something that could be in a number
static inline bool isNumericalCharacter(char X)
{
return (
(X >= '0' && X <= '9') || X == '-' || X == '+' || X == '.' ||
X == 'E' || X == 'e');
}

// forward declaration of the internal from_string function
static precise_unit unit_from_string_internal(
std::string unit_string,
Expand Down Expand Up @@ -491,31 +500,31 @@ static std::string generateUnitSequence(double mux, std::string seq)
switch (pw) {
case -1:
muxstr = getMultiplierString(1.0 / mux, noPrefix);
if (isNumericalCharacter(muxstr.front())) {
if (isNumericalStartCharacter(muxstr.front())) {
muxstr = getMultiplierString(mux, true);
}
break;
case -2:
muxstr = getMultiplierString(std::sqrt(1.0 / mux), noPrefix);
if (isNumericalCharacter(muxstr.front())) {
if (isNumericalStartCharacter(muxstr.front())) {
muxstr = getMultiplierString(mux, true);
}
break;
case -3:
muxstr = getMultiplierString(std::cbrt(1.0 / mux), noPrefix);
if (isNumericalCharacter(muxstr.front())) {
if (isNumericalStartCharacter(muxstr.front())) {
muxstr = getMultiplierString(mux, true);
}
break;
case 2:
muxstr = getMultiplierString(std::sqrt(mux), noPrefix);
if (isNumericalCharacter(muxstr.front())) {
if (isNumericalStartCharacter(muxstr.front())) {
muxstr = getMultiplierString(mux, true);
}
break;
case 3:
muxstr = getMultiplierString(std::cbrt(mux), noPrefix);
if (isNumericalCharacter(muxstr.front())) {
if (isNumericalStartCharacter(muxstr.front())) {
muxstr = getMultiplierString(mux, true);
}
break;
Expand Down Expand Up @@ -752,7 +761,7 @@ std::string
clean_unit_string(std::string propUnitString, std::uint32_t commodity)
{
using spair = std::tuple<const char*, const char*, int, int>;
static UNITS_CPP14_CONSTEXPR_OBJECT std::array<spair, 8> powerseq{{
static UNITS_CPP14_CONSTEXPR_OBJECT std::array<spair, 9> powerseq{{
spair{"Mm^3", "(1e9km^3)", 4, 8}, // this needs to happen before ^3^2
// conversions
spair{"^2^2", "^4", 4, 2},
Expand All @@ -763,6 +772,7 @@ std::string
// screwing things up
spair{"eflag*K", "degC", 7, 4},
spair{"*1*", "*", 3, 1},
spair{"*1/", "/", 3, 1},

}};
// run a few checks for unusual conditions
Expand Down Expand Up @@ -852,6 +862,25 @@ std::string
return propUnitString;
}

static const std::pair<const unit, std::string> nullret{invalid, std::string{}};

static std::pair<unit, std::string> find_unit_pair(unit un)
{
if (allowUserDefinedUnits.load(std::memory_order_acquire)) {
if (!user_defined_unit_names.empty()) {
auto fndud = user_defined_unit_names.find(un);
if (fndud != user_defined_unit_names.end()) {
return {fndud->first, fndud->second};
}
}
}
auto fnd = base_unit_names.find(un);
if (fnd != base_unit_names.end()) {
return {fnd->first, fnd->second};
}
return nullret;
}

static std::string find_unit(unit un)
{
if (allowUserDefinedUnits.load(std::memory_order_acquire)) {
Expand Down Expand Up @@ -903,6 +932,19 @@ static std::string
}

auto llunit = unit_cast(un);
// deal with situations where the cast unit is not normal but the precise
// one is
if (std::fpclassify(llunit.multiplier_f()) != FP_NORMAL) {
auto mstring = getMultiplierString(un.multiplier(), true);
un = precise_unit(un.base_units(), 1.0);
mstring.push_back('*');

mstring.append(to_string_internal(un, match_flags));
if (mstring.back() == '*') {
mstring.pop_back();
}
return mstring;
}
auto fnd = find_unit(llunit);
if (!fnd.empty()) {
return fnd;
Expand Down Expand Up @@ -933,13 +975,18 @@ static std::string
if (!un.base_units().root(2).has_e_flag() &&
!un.base_units().has_i_flag() && un.multiplier() > 0.0) {
auto squ = root(llunit, 2);
fnd = find_unit(squ);
if (!fnd.empty()) {
return fnd + "^2";
auto fndp = find_unit_pair(squ);
if (!fndp.second.empty()) {
if (fndp.first.pow(2) != llunit) {
return getMultiplierString(
(llunit / fndp.first.pow(2)).multiplier(), true) +
'*' + fndp.second + "^2";
}
return fndp.second + "^2";
}
fnd = find_unit(squ.inv());
if (!fnd.empty()) {
return std::string("1/") + fnd + "^2";
auto fndpi = find_unit_pair(squ.inv());
if (!fndpi.second.empty()) {
return std::string("1/") + fndpi.second + "^2";
}
}
/// Check for cubed units
Expand Down Expand Up @@ -968,7 +1015,7 @@ static std::string
fnd = find_unit(bunit.inv());
if (!fnd.empty()) {
auto prefix = generateUnitSequence(1.0 / un.multiplier(), fnd);
if (isNumericalCharacter(prefix.front())) {
if (isNumericalStartCharacter(prefix.front())) {
size_t cut;
double mx = getDoubleFromString(prefix, &cut);
return getMultiplierString(1.0 / mx, true) + "/" +
Expand Down Expand Up @@ -1018,7 +1065,7 @@ static std::string
urem.clear_flags();
urem.commodity(0);
if ((urem.multiplier() != 1.0) || (!urem.base_units().empty())) {
return to_string(urem) + '*' + cxstr;
return to_string_internal(urem, match_flags) + '*' + cxstr;
}
return cxstr;
}
Expand All @@ -1037,7 +1084,7 @@ static std::string
urem.clear_flags();
urem.commodity(0);
if ((urem.multiplier() != 1.0) || (!urem.base_units().empty())) {
return to_string(urem) + '*' + cxstr;
return to_string_internal(urem, match_flags) + '*' + cxstr;
}
return cxstr;
}
Expand All @@ -1056,7 +1103,7 @@ static std::string
urem.clear_flags();
urem.commodity(0);
if ((urem.multiplier() != 1.0) || (!urem.base_units().empty())) {
return to_string(urem) + '*' + cxstr;
return to_string_internal(urem, match_flags) + '*' + cxstr;
}
return cxstr;
}
Expand All @@ -1071,7 +1118,7 @@ static std::string
auto prefix = generateUnitSequence(ext.multiplier(), fnd);

auto str = prefix + '/' + tu.second;
if (!isNumericalCharacter(str.front())) {
if (!isNumericalStartCharacter(str.front())) {
return str;
}
if (beststr.empty() || str.size() < beststr.size()) {
Expand All @@ -1088,7 +1135,7 @@ static std::string
if (!fnd.empty()) {
auto prefix = generateUnitSequence(ext.multiplier(), fnd);
auto str = prefix + '*' + tu.second;
if (!isNumericalCharacter(str.front())) {
if (!isNumericalStartCharacter(str.front())) {
return str;
}
if (beststr.empty() || str.size() < beststr.size()) {
Expand All @@ -1104,7 +1151,7 @@ static std::string
fnd = find_unit(base.inv());
if (!fnd.empty()) {
auto prefix = generateUnitSequence(1.0 / ext.multiplier(), fnd);
if (isNumericalCharacter(prefix.front())) {
if (isNumericalStartCharacter(prefix.front())) {
size_t cut;
double mx = getDoubleFromString(prefix, &cut);
if (mx != 0.0) {
Expand Down Expand Up @@ -1136,7 +1183,7 @@ static std::string
str.push_back('*');
str.append(tu.second);
str.push_back(')');
if (!isNumericalCharacter(prefix.front())) {
if (!isNumericalStartCharacter(prefix.front())) {
return str;
}
if (beststr.empty() || str.size() < beststr.size()) {
Expand Down Expand Up @@ -1179,7 +1226,7 @@ std::string to_string(precise_measurement measure, std::uint32_t match_flags)
ss << measure.value();
ss << ' ';
auto str = to_string(measure.units(), match_flags);
if (isNumericalCharacter(str.front())) {
if (isNumericalStartCharacter(str.front())) {
str.insert(str.begin(), '(');
str.push_back(')');
}
Expand All @@ -1194,7 +1241,7 @@ std::string to_string(measurement measure, std::uint32_t match_flags)
ss << measure.value();
ss << ' ';
auto str = to_string(measure.units(), match_flags);
if (isNumericalCharacter(str.front())) {
if (isNumericalStartCharacter(str.front())) {
str.insert(str.begin(), '(');
str.push_back(')');
}
Expand Down Expand Up @@ -4551,7 +4598,7 @@ static bool cleanSpaces(std::string& unit_string, bool skipMultiply)
}
if (std::all_of(
unit_string.begin(), unit_string.begin() + fnd, [](char X) {
return isNumericalCharacter(X) || (X == '/') ||
return isNumericalStartCharacter(X) || (X == '/') ||
(X == '*');
})) {
unit_string[fnd] = '*';
Expand Down Expand Up @@ -4941,6 +4988,37 @@ static bool unicodeReplacement(std::string& unit_string)
return changed;
}

// 10*num usually means a power of 10 so in most cases replace it with 1e
// except it is not actually 10 or the thing after isn't a number
// but take into account the few cases where . notation is used
static void checkPowerOf10(std::string& unit_string)
{
auto fndP = unit_string.find("10*");
while (fndP != std::string::npos) {
if (unit_string.size() > fndP + 3 &&
isNumericalStartCharacter(unit_string[fndP + 3])) {
if (fndP == 0 || !isNumericalCharacter(unit_string[fndP - 1]) ||
(fndP >= 2 && unit_string[fndP - 1] == '.' &&
(unit_string[fndP - 2] < '0' ||
unit_string[fndP - 2] > '9'))) {
auto powerstr = unit_string.substr(fndP + 3);
if (looksLikeInteger(powerstr)) {
try {
int power = std::stoi(powerstr);
if (std::abs(power) <= 38) {
unit_string.replace(fndP, 3, "1e");
}
}
catch (const std::out_of_range&) {
// if it is a really big number we obviously skip it
}
}
}
}
fndP = unit_string.find("10*", fndP + 3);
}
}

// do some cleaning on the unit string to standardize formatting and deal with
// some extended ascii and unicode characters
static bool cleanUnitString(std::string& unit_string, std::uint32_t match_flags)
Expand Down Expand Up @@ -5052,27 +5130,7 @@ static bool cleanUnitString(std::string& unit_string, std::uint32_t match_flags)
// LCOV_EXCL_STOP
}

// 10*num usually means a power of 10
fndP = unit_string.find("10*");
while (fndP != std::string::npos) {
if (unit_string.size() > fndP + 3 &&
isNumericalCharacter(unit_string[fndP + 3])) {
auto powerstr = unit_string.substr(fndP + 3);
if (looksLikeInteger(powerstr)) {
try {
int power = std::stoi(powerstr);
if (std::abs(power) <= 38) {
unit_string.replace(fndP, 3, "1e");
}
}
catch (const std::out_of_range&) { // if it is a really big
// number we obviously
// skip it
}
}
}
fndP = unit_string.find("10*", fndP + 3);
}
checkPowerOf10(unit_string);
} else {
auto fndP = unit_string.find("of(");
if (fndP != std::string::npos) {
Expand Down