Commit e663daa1 authored by Dominik Charousset's avatar Dominik Charousset Committed by Dominik Charousset

Fix integer overflow check in number parser

parent d57d0715
......@@ -19,7 +19,10 @@
#pragma once
#include <limits>
#include "caf/detail/parser/ascii_to_int.hpp"
#include "caf/detail/type_traits.hpp"
namespace caf {
namespace detail {
......@@ -29,11 +32,26 @@ namespace parser {
// @returns `false` on an overflow, otherwise `true`.
// @pre `isdigit(c) || (Base == 16 && isxdigit(c))`
template <int Base, class T>
bool add_ascii(T& x, char c) {
bool add_ascii(T& x, char c, enable_if_tt<std::is_integral<T>, int> u = 0) {
CAF_IGNORE_UNUSED(u);
if (x > (std::numeric_limits<T>::max() / Base))
return false;
x *= Base;
ascii_to_int<Base, T> f;
auto y = f(c);
if (x > (std::numeric_limits<T>::max() - y))
return false;
x += y;
return true;
}
template <int Base, class T>
bool add_ascii(T& x, char c,
enable_if_tt<std::is_floating_point<T>, int> u = 0) {
CAF_IGNORE_UNUSED(u);
ascii_to_int<Base, T> f;
auto before = x;
x = (x * Base) + f(c);
return before <= x;
return true;
}
} // namespace parser
......
......@@ -19,7 +19,10 @@
#pragma once
#include <limits>
#include "caf/detail/parser/ascii_to_int.hpp"
#include "caf/detail/type_traits.hpp"
namespace caf {
namespace detail {
......@@ -29,11 +32,26 @@ namespace parser {
// @returns `false` on an underflow, otherwise `true`.
// @pre `isdigit(c) || (Base == 16 && isxdigit(c))`
template <int Base, class T>
bool sub_ascii(T& x, char c) {
bool sub_ascii(T& x, char c, enable_if_tt<std::is_integral<T>, int> u = 0) {
CAF_IGNORE_UNUSED(u);
if (x < (std::numeric_limits<T>::min() / Base))
return false;
x *= Base;
ascii_to_int<Base, T> f;
auto y = f(c);
if (x < (std::numeric_limits<T>::min() + y))
return false;
x -= y;
return true;
}
template <int Base, class T>
bool sub_ascii(T& x, char c,
enable_if_tt<std::is_floating_point<T>, int> u = 0) {
CAF_IGNORE_UNUSED(u);
ascii_to_int<Base, T> f;
auto before = x;
x = (x * Base) - f(c);
return before >= x;
return true;
}
} // namespace parser
......
......@@ -42,13 +42,34 @@ CAF_TEST(constructing) {
CAF_CHECK_EQUAL(zero.bits(), 0u);
}
CAF_TEST(to and from string) {
ipv4_address x;
auto err = parse("255.255.255.255", x);
CAF_CHECK_EQUAL(err, pec::success);
CAF_CHECK_EQUAL(x.bits(), 0xFFFFFFFF);
CAF_CHECK_EQUAL(to_string(x), "255.255.255.255");
CAF_CHECK_EQUAL(x, addr(255, 255, 255, 255));
CAF_TEST(to string) {
CAF_CHECK_EQUAL(to_string(addr(255, 255, 255, 255)), "255.255.255.255");
}
CAF_TEST(from string - valid inputs) {
auto from_string = [](string_view str) -> expected<ipv4_address> {
ipv4_address result;
if (auto err = parse(str, result))
return err;
return result;
};
CAF_CHECK_EQUAL(from_string("136.12.12.12"), addr(136, 12, 12, 12));
CAF_CHECK_EQUAL(from_string("255.255.255.255"), addr(255, 255, 255, 255));
}
CAF_TEST(from string - invalid inputs) {
auto should_fail = [](string_view str) {
ipv4_address result;
auto err = parse(str, result);
if (!err)
CAF_ERROR("error while parsing "
<< str << ", expected an error but got: " << to_string(result));
};
should_fail("256.12.12.12");
should_fail("1136.12.12.12");
should_fail("1137.12.12.12");
should_fail("1279.12.12.12");
should_fail("1280.12.12.12");
}
CAF_TEST(properties) {
......
......@@ -22,9 +22,12 @@
#include "caf/test/unit_test.hpp"
#include "caf/variant.hpp"
#include "caf/detail/parser/add_ascii.hpp"
#include "caf/detail/parser/read_number.hpp"
#include "caf/detail/parser/sub_ascii.hpp"
#include "caf/expected.hpp"
#include "caf/pec.hpp"
#include "caf/variant.hpp"
using namespace caf;
......@@ -96,6 +99,51 @@ struct fixture {
CAF_TEST_FIXTURE_SCOPE(read_number_tests, fixture)
CAF_TEST(add ascii - unsigned) {
using detail::parser::add_ascii;
auto rd = [](string_view str) -> expected<uint8_t> {
uint8_t x = 0;
for (auto c : str)
if (!add_ascii<10>(x, c))
return pec::integer_overflow;
return x;
};
for (int i = 0; i < 256; ++i)
CAF_CHECK_EQUAL(rd(std::to_string(i)), static_cast<uint8_t>(i));
for (int i = 256; i < 513; ++i)
CAF_CHECK_EQUAL(rd(std::to_string(i)), pec::integer_overflow);
}
CAF_TEST(add ascii - signed) {
auto rd = [](string_view str) -> expected<int8_t> {
int8_t x = 0;
for (auto c : str)
if (!detail::parser::add_ascii<10>(x, c))
return pec::integer_overflow;
return x;
};
for (int i = 0; i < 128; ++i)
CAF_CHECK_EQUAL(rd(std::to_string(i)), static_cast<int8_t>(i));
for (int i = 128; i < 513; ++i)
CAF_CHECK_EQUAL(rd(std::to_string(i)), pec::integer_overflow);
}
CAF_TEST(sub ascii) {
auto rd = [](string_view str) -> expected<int8_t> {
int8_t x = 0;
for (auto c : str)
if (!detail::parser::sub_ascii<10>(x, c))
return pec::integer_underflow;
return x;
};
// Using sub_ascii in this way behaves as if we'd prefix the number with a
// minus sign, i.e., "123" will result in -123.
for (int i = 1; i < 129; ++i)
CAF_CHECK_EQUAL(rd(std::to_string(i)), static_cast<int8_t>(-i));
for (int i = 129; i < 513; ++i)
CAF_CHECK_EQUAL(rd(std::to_string(i)), pec::integer_underflow);
}
CAF_TEST(binary numbers) {
/* TODO: use this implementation when switching to C++14
CHECK_NUMBER(0b0);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment