зеркало из https://github.com/mozilla/pjs.git
482 строки
17 KiB
C++
482 строки
17 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
|
/* ***** BEGIN LICENSE BLOCK *****
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
*
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
* the License. You may obtain a copy of the License at
|
|
* http://www.mozilla.org/MPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
* for the specific language governing rights and limitations under the
|
|
* License.
|
|
*
|
|
* The Original Code is Mozilla code.
|
|
*
|
|
* The Initial Developer of the Original Code is the Mozilla Corporation.
|
|
* Portions created by the Initial Developer are Copyright (C) 2009
|
|
* the Initial Developer. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
* Benoit Jacob <bjacob@mozilla.com>
|
|
*
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
* the provisions above, a recipient may use your version of this file under
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
*
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
|
|
#include "CheckedInt.h"
|
|
#include <iostream>
|
|
|
|
namespace CheckedInt_test {
|
|
|
|
using namespace mozilla::CheckedInt_internal;
|
|
using mozilla::CheckedInt;
|
|
|
|
int g_tests_passed = 0;
|
|
int g_tests_failed = 0;
|
|
|
|
void verify_impl_function(bool x, bool expected,
|
|
const char* file, int line,
|
|
int T_size, bool T_is_signed)
|
|
{
|
|
if (x == expected) {
|
|
g_tests_passed++;
|
|
} else {
|
|
g_tests_failed++;
|
|
std::cerr << "Test failed at " << file << ":" << line;
|
|
std::cerr << " with T a ";
|
|
if(T_is_signed)
|
|
std::cerr << "signed";
|
|
else
|
|
std::cerr << "unsigned";
|
|
std::cerr << " " << CHAR_BIT*T_size << "-bit integer type" << std::endl;
|
|
}
|
|
}
|
|
|
|
#define VERIFY_IMPL(x, expected) \
|
|
verify_impl_function((x), (expected), __FILE__, __LINE__, sizeof(T), integer_traits<T>::is_signed)
|
|
|
|
#define VERIFY(x) VERIFY_IMPL(x, true)
|
|
#define VERIFY_IS_FALSE(x) VERIFY_IMPL(x, false)
|
|
#define VERIFY_IS_VALID(x) VERIFY_IMPL((x).valid(), PR_TRUE)
|
|
#define VERIFY_IS_INVALID(x) VERIFY_IMPL((x).valid(), PR_FALSE)
|
|
#define VERIFY_IS_VALID_IF(x,condition) VERIFY_IMPL((x).valid(), (condition))
|
|
|
|
template<typename T, unsigned int size = sizeof(T)>
|
|
struct test_twice_bigger_type
|
|
{
|
|
static void run()
|
|
{
|
|
VERIFY(integer_traits<T>::twice_bigger_type_is_supported);
|
|
VERIFY(sizeof(typename integer_traits<T>::twice_bigger_type)
|
|
== 2 * sizeof(T));
|
|
VERIFY(bool(integer_traits<
|
|
typename integer_traits<T>::twice_bigger_type
|
|
>::is_signed) == bool(integer_traits<T>::is_signed));
|
|
}
|
|
};
|
|
|
|
template<typename T>
|
|
struct test_twice_bigger_type<T, 8>
|
|
{
|
|
static void run()
|
|
{
|
|
VERIFY_IS_FALSE(integer_traits<T>::twice_bigger_type_is_supported);
|
|
}
|
|
};
|
|
|
|
|
|
template<typename T>
|
|
void test()
|
|
{
|
|
static bool already_run = false;
|
|
if (already_run) {
|
|
g_tests_failed++;
|
|
std::cerr << "You already tested this type. Copy/paste typo??" << std::endl;
|
|
return;
|
|
}
|
|
already_run = true;
|
|
|
|
VERIFY(integer_traits<T>::is_supported);
|
|
VERIFY(integer_traits<T>::size == sizeof(T));
|
|
enum{ is_signed = integer_traits<T>::is_signed };
|
|
VERIFY(bool(is_signed) == !bool(T(-1) > T(0)));
|
|
|
|
test_twice_bigger_type<T>::run();
|
|
|
|
CheckedInt<T> max_value(integer_traits<T>::max_value());
|
|
CheckedInt<T> min_value(integer_traits<T>::min_value());
|
|
|
|
// check min_value() and max_value(), since they are custom implementations and a mistake there
|
|
// could potentially NOT be caught by any other tests... while making everything wrong!
|
|
|
|
T bit = 1;
|
|
for(unsigned int i = 0; i < sizeof(T) * CHAR_BIT - 1; i++)
|
|
{
|
|
VERIFY((min_value.value() & bit) == 0);
|
|
bit <<= 1;
|
|
}
|
|
VERIFY((min_value.value() & bit) == (is_signed ? bit : T(0)));
|
|
VERIFY(max_value.value() == T(~(min_value.value())));
|
|
|
|
CheckedInt<T> zero(0);
|
|
CheckedInt<T> one(1);
|
|
CheckedInt<T> two(2);
|
|
CheckedInt<T> three(3);
|
|
CheckedInt<T> four(4);
|
|
|
|
/* addition / substraction checks */
|
|
|
|
VERIFY_IS_VALID(zero+zero);
|
|
VERIFY(zero+zero == zero);
|
|
VERIFY_IS_FALSE(zero+zero == one); // check that == doesn't always return true
|
|
VERIFY_IS_VALID(zero+one);
|
|
VERIFY(zero+one == one);
|
|
VERIFY_IS_VALID(one+one);
|
|
VERIFY(one+one == two);
|
|
|
|
CheckedInt<T> max_value_minus_one = max_value - one;
|
|
CheckedInt<T> max_value_minus_two = max_value - two;
|
|
VERIFY_IS_VALID(max_value_minus_one);
|
|
VERIFY_IS_VALID(max_value_minus_two);
|
|
VERIFY_IS_VALID(max_value_minus_one + one);
|
|
VERIFY_IS_VALID(max_value_minus_two + one);
|
|
VERIFY_IS_VALID(max_value_minus_two + two);
|
|
VERIFY(max_value_minus_one + one == max_value);
|
|
VERIFY(max_value_minus_two + one == max_value_minus_one);
|
|
VERIFY(max_value_minus_two + two == max_value);
|
|
|
|
VERIFY_IS_VALID(max_value + zero);
|
|
VERIFY_IS_VALID(max_value - zero);
|
|
VERIFY_IS_INVALID(max_value + one);
|
|
VERIFY_IS_INVALID(max_value + two);
|
|
VERIFY_IS_INVALID(max_value + max_value_minus_one);
|
|
VERIFY_IS_INVALID(max_value + max_value);
|
|
|
|
CheckedInt<T> min_value_plus_one = min_value + one;
|
|
CheckedInt<T> min_value_plus_two = min_value + two;
|
|
VERIFY_IS_VALID(min_value_plus_one);
|
|
VERIFY_IS_VALID(min_value_plus_two);
|
|
VERIFY_IS_VALID(min_value_plus_one - one);
|
|
VERIFY_IS_VALID(min_value_plus_two - one);
|
|
VERIFY_IS_VALID(min_value_plus_two - two);
|
|
VERIFY(min_value_plus_one - one == min_value);
|
|
VERIFY(min_value_plus_two - one == min_value_plus_one);
|
|
VERIFY(min_value_plus_two - two == min_value);
|
|
|
|
CheckedInt<T> min_value_minus_one = min_value - one;
|
|
VERIFY_IS_VALID(min_value + zero);
|
|
VERIFY_IS_VALID(min_value - zero);
|
|
VERIFY_IS_INVALID(min_value - one);
|
|
VERIFY_IS_INVALID(min_value - two);
|
|
VERIFY_IS_INVALID(min_value - min_value_minus_one);
|
|
VERIFY_IS_VALID(min_value - min_value);
|
|
|
|
CheckedInt<T> max_value_over_two = max_value / two;
|
|
VERIFY_IS_VALID(max_value_over_two + max_value_over_two);
|
|
VERIFY_IS_VALID(max_value_over_two + one);
|
|
VERIFY((max_value_over_two + one) - one == max_value_over_two);
|
|
VERIFY_IS_VALID(max_value_over_two - max_value_over_two);
|
|
VERIFY(max_value_over_two - max_value_over_two == zero);
|
|
|
|
CheckedInt<T> min_value_over_two = min_value / two;
|
|
VERIFY_IS_VALID(min_value_over_two + min_value_over_two);
|
|
VERIFY_IS_VALID(min_value_over_two + one);
|
|
VERIFY((min_value_over_two + one) - one == min_value_over_two);
|
|
VERIFY_IS_VALID(min_value_over_two - min_value_over_two);
|
|
VERIFY(min_value_over_two - min_value_over_two == zero);
|
|
|
|
VERIFY_IS_INVALID(min_value - one);
|
|
VERIFY_IS_INVALID(min_value - two);
|
|
|
|
if (is_signed) {
|
|
VERIFY_IS_INVALID(min_value + min_value);
|
|
VERIFY_IS_INVALID(min_value_over_two + min_value_over_two + min_value_over_two);
|
|
VERIFY_IS_INVALID(zero - min_value + min_value);
|
|
VERIFY_IS_INVALID(one - min_value + min_value);
|
|
}
|
|
|
|
/* unary operator- checks */
|
|
|
|
CheckedInt<T> neg_one = -one;
|
|
CheckedInt<T> neg_two = -two;
|
|
|
|
if (is_signed) {
|
|
VERIFY_IS_VALID(-max_value);
|
|
VERIFY_IS_VALID(-max_value - one);
|
|
VERIFY_IS_VALID(neg_one);
|
|
VERIFY_IS_VALID(-max_value + neg_one);
|
|
VERIFY_IS_VALID(neg_one + one);
|
|
VERIFY(neg_one + one == zero);
|
|
VERIFY_IS_VALID(neg_two);
|
|
VERIFY_IS_VALID(neg_one + neg_one);
|
|
VERIFY(neg_one + neg_one == neg_two);
|
|
} else {
|
|
VERIFY_IS_INVALID(neg_one);
|
|
}
|
|
|
|
/* multiplication checks */
|
|
|
|
VERIFY_IS_VALID(zero*zero);
|
|
VERIFY(zero*zero == zero);
|
|
VERIFY_IS_VALID(zero*one);
|
|
VERIFY(zero*one == zero);
|
|
VERIFY_IS_VALID(one*zero);
|
|
VERIFY(one*zero == zero);
|
|
VERIFY_IS_VALID(one*one);
|
|
VERIFY(one*one == one);
|
|
VERIFY_IS_VALID(one*three);
|
|
VERIFY(one*three == three);
|
|
VERIFY_IS_VALID(two*two);
|
|
VERIFY(two*two == four);
|
|
|
|
VERIFY_IS_INVALID(max_value * max_value);
|
|
VERIFY_IS_INVALID(max_value_over_two * max_value);
|
|
VERIFY_IS_INVALID(max_value_over_two * max_value_over_two);
|
|
|
|
CheckedInt<T> max_value_approx_sqrt(T(T(1) << (CHAR_BIT*sizeof(T)/2)));
|
|
|
|
VERIFY_IS_VALID(max_value_approx_sqrt);
|
|
VERIFY_IS_VALID(max_value_approx_sqrt * two);
|
|
VERIFY_IS_INVALID(max_value_approx_sqrt * max_value_approx_sqrt);
|
|
VERIFY_IS_INVALID(max_value_approx_sqrt * max_value_approx_sqrt * max_value_approx_sqrt);
|
|
|
|
if (is_signed) {
|
|
VERIFY_IS_INVALID(min_value * min_value);
|
|
VERIFY_IS_INVALID(min_value_over_two * min_value);
|
|
VERIFY_IS_INVALID(min_value_over_two * min_value_over_two);
|
|
|
|
CheckedInt<T> min_value_approx_sqrt = -max_value_approx_sqrt;
|
|
|
|
VERIFY_IS_VALID(min_value_approx_sqrt);
|
|
VERIFY_IS_VALID(min_value_approx_sqrt * two);
|
|
VERIFY_IS_INVALID(min_value_approx_sqrt * max_value_approx_sqrt);
|
|
VERIFY_IS_INVALID(min_value_approx_sqrt * min_value_approx_sqrt);
|
|
}
|
|
|
|
// make sure to check all 4 paths in signed multiplication validity check.
|
|
// test positive * positive
|
|
VERIFY_IS_VALID(max_value * one);
|
|
VERIFY(max_value * one == max_value);
|
|
VERIFY_IS_INVALID(max_value * two);
|
|
VERIFY_IS_VALID(max_value_over_two * two);
|
|
VERIFY((max_value_over_two + max_value_over_two) == (max_value_over_two * two));
|
|
|
|
if (is_signed) {
|
|
// test positive * negative
|
|
VERIFY_IS_VALID(max_value * neg_one);
|
|
VERIFY_IS_VALID(-max_value);
|
|
VERIFY(max_value * neg_one == -max_value);
|
|
VERIFY_IS_VALID(one * min_value);
|
|
VERIFY_IS_INVALID(max_value * neg_two);
|
|
VERIFY_IS_VALID(max_value_over_two * neg_two);
|
|
VERIFY_IS_VALID(two * min_value_over_two);
|
|
VERIFY_IS_VALID((max_value_over_two + one) * neg_two);
|
|
VERIFY_IS_INVALID((max_value_over_two + two) * neg_two);
|
|
VERIFY_IS_INVALID(two * (min_value_over_two - one));
|
|
|
|
// test negative * positive
|
|
VERIFY_IS_VALID(min_value * one);
|
|
VERIFY_IS_VALID(min_value_plus_one * one);
|
|
VERIFY_IS_INVALID(min_value * two);
|
|
VERIFY_IS_VALID(min_value_over_two * two);
|
|
VERIFY(min_value_over_two * two == min_value);
|
|
VERIFY_IS_INVALID((min_value_over_two - one) * neg_two);
|
|
VERIFY_IS_INVALID(neg_two * max_value);
|
|
VERIFY_IS_VALID(min_value_over_two * two);
|
|
VERIFY(min_value_over_two * two == min_value);
|
|
VERIFY_IS_VALID(neg_two * max_value_over_two);
|
|
VERIFY_IS_INVALID((min_value_over_two - one) * two);
|
|
VERIFY_IS_VALID(neg_two * (max_value_over_two + one));
|
|
VERIFY_IS_INVALID(neg_two * (max_value_over_two + two));
|
|
|
|
// test negative * negative
|
|
VERIFY_IS_INVALID(min_value * neg_one);
|
|
VERIFY_IS_VALID(min_value_plus_one * neg_one);
|
|
VERIFY(min_value_plus_one * neg_one == max_value);
|
|
VERIFY_IS_INVALID(min_value * neg_two);
|
|
VERIFY_IS_INVALID(min_value_over_two * neg_two);
|
|
VERIFY_IS_INVALID(neg_one * min_value);
|
|
VERIFY_IS_VALID(neg_one * min_value_plus_one);
|
|
VERIFY(neg_one * min_value_plus_one == max_value);
|
|
VERIFY_IS_INVALID(neg_two * min_value);
|
|
VERIFY_IS_INVALID(neg_two * min_value_over_two);
|
|
}
|
|
|
|
/* division checks */
|
|
|
|
VERIFY_IS_VALID(one / one);
|
|
VERIFY(one / one == one);
|
|
VERIFY_IS_VALID(three / three);
|
|
VERIFY(three / three == one);
|
|
VERIFY_IS_VALID(four / two);
|
|
VERIFY(four / two == two);
|
|
VERIFY((four*three)/four == three);
|
|
|
|
// check that div by zero is invalid
|
|
VERIFY_IS_INVALID(zero / zero);
|
|
VERIFY_IS_INVALID(one / zero);
|
|
VERIFY_IS_INVALID(two / zero);
|
|
VERIFY_IS_INVALID(neg_one / zero);
|
|
VERIFY_IS_INVALID(max_value / zero);
|
|
VERIFY_IS_INVALID(min_value / zero);
|
|
|
|
if (is_signed) {
|
|
// check that min_value / -1 is invalid
|
|
VERIFY_IS_INVALID(min_value / neg_one);
|
|
|
|
// check that the test for div by -1 isn't banning other numerators than min_value
|
|
VERIFY_IS_VALID(one / neg_one);
|
|
VERIFY_IS_VALID(zero / neg_one);
|
|
VERIFY_IS_VALID(neg_one / neg_one);
|
|
VERIFY_IS_VALID(max_value / neg_one);
|
|
}
|
|
|
|
/* check that invalidity is correctly preserved by arithmetic ops */
|
|
|
|
CheckedInt<T> some_invalid = max_value + max_value;
|
|
VERIFY_IS_INVALID(some_invalid + zero);
|
|
VERIFY_IS_INVALID(some_invalid - zero);
|
|
VERIFY_IS_INVALID(zero + some_invalid);
|
|
VERIFY_IS_INVALID(zero - some_invalid);
|
|
VERIFY_IS_INVALID(-some_invalid);
|
|
VERIFY_IS_INVALID(some_invalid * zero);
|
|
VERIFY_IS_INVALID(some_invalid * one);
|
|
VERIFY_IS_INVALID(zero * some_invalid);
|
|
VERIFY_IS_INVALID(one * some_invalid);
|
|
VERIFY_IS_INVALID(some_invalid / zero);
|
|
VERIFY_IS_INVALID(some_invalid / one);
|
|
VERIFY_IS_INVALID(zero / some_invalid);
|
|
VERIFY_IS_INVALID(one / some_invalid);
|
|
VERIFY_IS_INVALID(some_invalid + some_invalid);
|
|
VERIFY_IS_INVALID(some_invalid - some_invalid);
|
|
VERIFY_IS_INVALID(some_invalid * some_invalid);
|
|
VERIFY_IS_INVALID(some_invalid / some_invalid);
|
|
|
|
/* check that mixing checked integers with plain integers in expressions is allowed */
|
|
|
|
VERIFY(one + T(2) == three);
|
|
VERIFY(2 + one == three);
|
|
{
|
|
CheckedInt<T> x = one;
|
|
x += 2;
|
|
VERIFY(x == three);
|
|
}
|
|
VERIFY(two - 1 == one);
|
|
VERIFY(2 - one == one);
|
|
{
|
|
CheckedInt<T> x = two;
|
|
x -= 1;
|
|
VERIFY(x == one);
|
|
}
|
|
VERIFY(one * 2 == two);
|
|
VERIFY(2 * one == two);
|
|
{
|
|
CheckedInt<T> x = one;
|
|
x *= 2;
|
|
VERIFY(x == two);
|
|
}
|
|
VERIFY(four / 2 == two);
|
|
VERIFY(4 / two == two);
|
|
{
|
|
CheckedInt<T> x = four;
|
|
x /= 2;
|
|
VERIFY(x == two);
|
|
}
|
|
|
|
VERIFY(one == 1);
|
|
VERIFY(1 == one);
|
|
VERIFY_IS_FALSE(two == 1);
|
|
VERIFY_IS_FALSE(1 == two);
|
|
VERIFY_IS_FALSE(some_invalid == 1);
|
|
VERIFY_IS_FALSE(1 == some_invalid);
|
|
|
|
/* Check that construction of CheckedInt from an integer value of a mismatched type is checked */
|
|
|
|
#define VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(U) \
|
|
{ \
|
|
bool is_U_signed = integer_traits<U>::is_signed; \
|
|
VERIFY_IS_VALID(CheckedInt<T>(U(0))); \
|
|
VERIFY_IS_VALID(CheckedInt<T>(U(1))); \
|
|
VERIFY_IS_VALID(CheckedInt<T>(U(100))); \
|
|
if (is_U_signed) \
|
|
VERIFY_IS_VALID_IF(CheckedInt<T>(U(-1)), is_signed); \
|
|
if (sizeof(U) > sizeof(T)) \
|
|
VERIFY_IS_INVALID(CheckedInt<T>(U(integer_traits<T>::max_value())+1)); \
|
|
VERIFY_IS_VALID_IF(CheckedInt<T>(integer_traits<U>::max_value()), \
|
|
(sizeof(T) > sizeof(U) || ((sizeof(T) == sizeof(U)) && (is_U_signed || !is_signed)))); \
|
|
VERIFY_IS_VALID_IF(CheckedInt<T>(integer_traits<U>::min_value()), \
|
|
is_U_signed == false ? 1 : \
|
|
bool(is_signed) == false ? 0 : \
|
|
sizeof(T) >= sizeof(U)); \
|
|
}
|
|
VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(PRInt8)
|
|
VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(PRUint8)
|
|
VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(PRInt16)
|
|
VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(PRUint16)
|
|
VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(PRInt32)
|
|
VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(PRUint32)
|
|
VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(PRInt64)
|
|
VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(PRUint64)
|
|
|
|
/* Test increment/decrement operators */
|
|
|
|
CheckedInt<T> x, y;
|
|
x = one;
|
|
y = x++;
|
|
VERIFY(x == two);
|
|
VERIFY(y == one);
|
|
x = one;
|
|
y = ++x;
|
|
VERIFY(x == two);
|
|
VERIFY(y == two);
|
|
x = one;
|
|
y = x--;
|
|
VERIFY(x == zero);
|
|
VERIFY(y == one);
|
|
x = one;
|
|
y = --x;
|
|
VERIFY(x == zero);
|
|
VERIFY(y == zero);
|
|
x = max_value;
|
|
VERIFY_IS_VALID(x++);
|
|
VERIFY_IS_INVALID(++x);
|
|
x = min_value;
|
|
VERIFY_IS_VALID(x--);
|
|
VERIFY_IS_INVALID(--x);
|
|
}
|
|
|
|
} // end namespace CheckedInt_test
|
|
|
|
int main()
|
|
{
|
|
CheckedInt_test::test<PRInt8>();
|
|
CheckedInt_test::test<PRUint8>();
|
|
CheckedInt_test::test<PRInt16>();
|
|
CheckedInt_test::test<PRUint16>();
|
|
CheckedInt_test::test<PRInt32>();
|
|
CheckedInt_test::test<PRUint32>();
|
|
CheckedInt_test::test<PRInt64>();
|
|
CheckedInt_test::test<PRUint64>();
|
|
|
|
std::cerr << CheckedInt_test::g_tests_failed << " tests failed, "
|
|
<< CheckedInt_test::g_tests_passed << " tests passed out of "
|
|
<< CheckedInt_test::g_tests_failed + CheckedInt_test::g_tests_passed
|
|
<< " tests." << std::endl;
|
|
|
|
return CheckedInt_test::g_tests_failed > 0;
|
|
}
|