/********************************************** | |

* 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; | |

} |