blob: af7b56f145c6ab1c312ab09a1ab153edeee7b15a [file] [log] [blame]
/**********************************************
* This module implements integral arithmetic primitives that check
* for out-of-range results.
* This is a translation to C++ of D's core.checkedint
*
* Integral arithmetic operators operate on fixed width types.
* Results that are not representable in those fixed widths are silently
* truncated to fit.
* This module offers integral arithmetic primitives that produce the
* same results, but set an 'overflow' flag when such truncation occurs.
* The setting is sticky, meaning that numerous operations can be cascaded
* and then the flag need only be checked at the end.
* Whether the operation is signed or unsigned is indicated by an 's' or 'u'
* suffix, respectively. While this could be achieved without such suffixes by
* using overloading on the signedness of the types, the suffix makes it clear
* which is happening without needing to examine the types.
*
* While the generic versions of these functions are computationally expensive
* relative to the cost of the operation itself, compiler implementations are free
* to recognize them and generate equivalent and faster code.
*
* References: $(LINK2 http://blog.regehr.org/archives/1139, Fast Integer Overflow Checks)
* Copyright: Copyright (C) 2014-2021 by The D Language Foundation, All Rights Reserved
* License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
* Authors: Walter Bright
* Source: https://github.com/D-Programming-Language/dmd/blob/master/src/root/checkedint.c
*/
#include "dsystem.h"
#include "checkedint.h"
/*******************************
* Add two signed integers, checking for overflow.
*
* The overflow is sticky, meaning a sequence of operations can
* be done and overflow need only be checked at the end.
* Params:
* x = left operand
* y = right operand
* overflow = set if an overflow occurs, is not affected otherwise
* Returns:
* the sum
*/
int adds(int x, int y, bool& overflow)
{
int64_t r = (int64_t)x + (int64_t)y;
if (r < INT32_MIN || r > INT32_MAX)
overflow = true;
return (int)r;
}
/// ditto
int64_t adds(int64_t x, int64_t y, bool& overflow)
{
int64_t r = (uint64_t)x + (uint64_t)y;
if ((x < 0 && y < 0 && r >= 0) ||
(x >= 0 && y >= 0 && r < 0))
overflow = true;
return r;
}
/*******************************
* Add two unsigned integers, checking for overflow (aka carry).
*
* The overflow is sticky, meaning a sequence of operations can
* be done and overflow need only be checked at the end.
* Params:
* x = left operand
* y = right operand
* overflow = set if an overflow occurs, is not affected otherwise
* Returns:
* the sum
*/
unsigned addu(unsigned x, unsigned y, bool& overflow)
{
unsigned r = x + y;
if (r < x || r < y)
overflow = true;
return r;
}
/// ditto
uint64_t addu(uint64_t x, uint64_t y, bool& overflow)
{
uint64_t r = x + y;
if (r < x || r < y)
overflow = true;
return r;
}
/*******************************
* Subtract two signed integers, checking for overflow.
*
* The overflow is sticky, meaning a sequence of operations can
* be done and overflow need only be checked at the end.
* Params:
* x = left operand
* y = right operand
* overflow = set if an overflow occurs, is not affected otherwise
* Returns:
* the sum
*/
int subs(int x, int y, bool& overflow)
{
int64_t r = (int64_t)x - (int64_t)y;
if (r < INT32_MIN || r > INT32_MAX)
overflow = true;
return (int)r;
}
/// ditto
int64_t subs(int64_t x, int64_t y, bool& overflow)
{
int64_t r = (uint64_t)x - (uint64_t)y;
if ((x < 0 && y >= 0 && r >= 0) ||
(x >= 0 && y < 0 && (r < 0 || y == INT64_MIN)))
overflow = true;
return r;
}
/*******************************
* Subtract two unsigned integers, checking for overflow (aka borrow).
*
* The overflow is sticky, meaning a sequence of operations can
* be done and overflow need only be checked at the end.
* Params:
* x = left operand
* y = right operand
* overflow = set if an overflow occurs, is not affected otherwise
* Returns:
* the sum
*/
unsigned subu(unsigned x, unsigned y, bool& overflow)
{
if (x < y)
overflow = true;
return x - y;
}
/// ditto
uint64_t subu(uint64_t x, uint64_t y, bool& overflow)
{
if (x < y)
overflow = true;
return x - y;
}
/***********************************************
* Negate an integer.
*
* Params:
* x = operand
* overflow = set if x cannot be negated, is not affected otherwise
* Returns:
* the negation of x
*/
int negs(int x, bool& overflow)
{
if (x == (int)INT32_MIN)
overflow = true;
return -x;
}
/// ditto
int64_t negs(int64_t x, bool& overflow)
{
if (x == INT64_MIN)
overflow = true;
return -x;
}
/*******************************
* Multiply two signed integers, checking for overflow.
*
* The overflow is sticky, meaning a sequence of operations can
* be done and overflow need only be checked at the end.
* Params:
* x = left operand
* y = right operand
* overflow = set if an overflow occurs, is not affected otherwise
* Returns:
* the sum
*/
int muls(int x, int y, bool& overflow)
{
int64_t r = (int64_t)x * (int64_t)y;
if (r < INT32_MIN || r > INT32_MAX)
overflow = true;
return (int)r;
}
/// ditto
int64_t muls(int64_t x, int64_t y, bool& overflow)
{
int64_t r = (uint64_t)x * (uint64_t)y;
int64_t not0or1 = ~(int64_t)1;
if ((x & not0or1) && ((r == y) ? r : (r / x) != y))
overflow = true;
return r;
}
/*******************************
* Multiply two unsigned integers, checking for overflow (aka carry).
*
* The overflow is sticky, meaning a sequence of operations can
* be done and overflow need only be checked at the end.
* Params:
* x = left operand
* y = right operand
* overflow = set if an overflow occurs, is not affected otherwise
* Returns:
* the sum
*/
unsigned mulu(unsigned x, unsigned y, bool& overflow)
{
uint64_t r = (uint64_t)x * (uint64_t)y;
if (r > UINT32_MAX)
overflow = true;
return (unsigned)r;
}
/// ditto
uint64_t mulu(uint64_t x, uint64_t y, bool& overflow)
{
uint64_t r = x * y;
if (x && (r / x) != y)
overflow = true;
return r;
}