/* Copyright (C) 2021,2022 fef . All rights reserved. */ #pragma once #ifndef __clang__ #error "Unsupported compiler, please use clang" #endif #include #include /** * @defgroup arith Safe integer arithmetic primitives * * @{ */ /** * @brief Perform an integer addition, store the result in `outptr`, * and return whether the operation resulted in an overflow. * * @param outptr Where to store the result * @param a First summand * @param b Second summand * @returns `true` if the addition overflowed, `false` otherwise */ #define add_overflow(outptr, a, b) __builtin_add_overflow(a, b, outptr) /** * @brief Add two `signed int`s, store the sum in `outptr`, * and return whether the operation overflowed. * * @param result Where to store the result * @param a First summand * @param b Second summand * @returns `true` if the addition overflowed, `false` otherwise */ __always_inline bool iadd_overflow(signed int *result, signed int a, signed int b) { return __builtin_sadd_overflow(a, b, result); } /** * @brief Add two `signed long`s, store the sum in `outptr`, * and return whether the operation overflowed. * * @param result Where to store the result * @param a First summand * @param b Second summand * @returns `true` if the addition overflowed, `false` otherwise */ __always_inline bool iaddl_overflow(signed long *result, signed int a, signed int b) { return __builtin_saddl_overflow(a, b, result); } /** * @brief Add the two `signed long long` arguments `a` and `b`, * store the sum in `outptr`, and return whether the operation overflowed. * * @param result Where to store the result * @param a First summand * @param b Second summand * @returns `true` if the addition overflowed, `false` otherwise */ __always_inline bool iaddll_overflow(signed long long *result, signed int a, signed int b) { return __builtin_saddll_overflow(a, b, result); } /** * @brief Add the two `unsigned int` arguments `a` and `b`, * store the sum in `outptr`, and return whether the operation overflowed. * * @param result Where to store the result * @param a First summand * @param b Second summand * @returns `true` if the addition overflowed, `false` otherwise */ __always_inline bool uadd_overflow(unsigned int *result, unsigned int a, unsigned int b) { return __builtin_uadd_overflow(a, b, result); } /** * @brief Add the two `unsigned long` arguments `a` and `b`, * store the sum in `outptr`, and return whether the operation overflowed. * * @param result Where to store the result * @param a First summand * @param b Second summand * @returns `true` if the addition overflowed, `false` otherwise */ __always_inline bool uaddl_overflow(unsigned long *result, unsigned long a, unsigned long b) { return __builtin_uaddl_overflow(a, b, result); } /** * @brief Add the two `unsigned long long` arguments `a` and `b`, * store the sum in `outptr`, and return whether the operation overflowed. * * @param result Where to store the result * @param a First summand * @param b Second summand * @returns `true` if the addition overflowed, `false` otherwise */ __always_inline bool uaddll_overflow(unsigned long long *result, unsigned long long a, unsigned long long b) { return __builtin_uaddll_overflow(a, b, result); } /** * @brief Test whether the sum of `a` and `b` would overflow for * a given `type` of result operand, but discharge the result. * * @param type Type that would store the result * @param a First summand * @param b Second summand * @returns `true` if the sum would **not** fit inside `type`, `false` otherwise */ #define add_test_overflow(type, a, b) __builtin_add_p(a, b, type) /** * @brief Perform an integer subtraction and store the result to `outptr`, * and return whether the operation resulted in an underflow. * * @param outptr Where to store the result * @param a The minuend * @param b The subtrahend * @returns `true` if the difference underflowed, `false` otherwise */ #define sub_underflow(outptr, a, b) __builtin_sub_overflow(a, b, outptr) /** * @brief Add two `signed int`s, store the result, * and return whether the operation overflowed. * * @param result Where to store the result * @param a The minuend * @param b The subtrahend * @returns `true` if the difference underflowed, `false` otherwise */ __always_inline bool isub_underflow(signed int *result, signed int a, signed int b) { return __builtin_ssub_overflow(a, b, result); } /** * @brief Add two `signed long`s, store the result, * and return whether the operation overflowed. * * @param result Where to store the result * @param a The minuend * @param b The subtrahend * @returns `true` if the difference underflowed, `false` otherwise */ __always_inline bool isubl_underflow(signed long *result, signed long a, signed long b) { return __builtin_ssubl_overflow(a, b, result); } /** * @brief Add the two `signed long long` arguments `a` and `b`, * store the result, and return whether the operation overflowed. * * @param result Where to store the result * @param a The minuend * @param b The subtrahend * @returns `true` if the difference underflowed, `false` otherwise */ __always_inline bool isubll_underflow(signed long long *result, signed long long a, signed long long b) { return __builtin_ssubll_overflow(a, b, result); } /** * @brief Add the two `unsigned int` arguments `a` and `b`, * store the result, and return whether the operation overflowed. * * @param result Where to store the result * @param a The minuend * @param b The subtrahend * @returns `true` if the difference underflowed, `false` otherwise */ __always_inline bool usub_underflow(unsigned int *result, unsigned int a, unsigned int b) { return __builtin_usub_overflow(a, b, result); } /** * @brief Add the two `unsigned long` arguments `a` and `b`, * store the result, and return whether the operation overflowed. * * @param result Where to store the result * @param a The minuend * @param b The subtrahend * @returns `true` if the difference underflowed, `false` otherwise */ __always_inline bool usubl_underflow(unsigned long *result, unsigned long a, unsigned long b) { return __builtin_usubl_overflow(a, b, result); } /** * @brief Add the two `unsigned long long` arguments `a` and `b`, * store the result, and return whether the operation overflowed. * * @param result Where to store the result * @param a The minuend * @param b The subtrahend * @returns `true` if the difference underflowed, `false` otherwise */ __always_inline bool usubll_underflow(unsigned long long *result, unsigned long long a, unsigned long long b) { return __builtin_usubll_overflow(a, b, result); } /** * @brief Test whether the difference between `a` and `b` would underflow for * a given `type` of result operand, but discharge the result. * * @param type Type that would store the result * @param a The minuend * @param b The subtrahend * @returns `true` if the difference would underflow, `false` otherwise */ #define sub_test_underflow(type, a, b) __builtin_sub_p(a, b, type) /** * @brief Perform an integer multiplication, store the result in `outptr`, * and return whether the operation resulted in an overflow. * * @param outptr Where to store the result * @param a First factor * @param b Second factor * @returns `true` if the multiplication overflowed, `false` otherwise */ #define mul_overflow(outptr, a, b) __builtin_mul_overflow(a, b, outptr) /** * @brief Multiply two `signed int`s, store the result, * and return whether the operation overflowed. * * @param outptr Where to store the result * @param a First factor * @param b Second factor * @returns `true` if the addition overflowed, `false` otherwise */ __always_inline bool imul_overflow(signed int *result, signed int a, signed int b) { return __builtin_smul_overflow(a, b, result); } /** * @brief Multiply two `signed long`s, store the result, * and return whether the operation overflowed. * * @param outptr Where to store the result * @param a First factor * @param b Second factor * @returns `true` if the addition overflowed, `false` otherwise */ __always_inline bool imul_l_overflow(signed long *result, signed int a, signed int b) { return __builtin_smull_overflow(a, b, result); } /** * @brief Multiply the two `signed long long` arguments `a` and `b`, * store the result, and return whether the operation overflowed. * * @param outptr Where to store the result * @param a First factor * @param b Second factor * @returns `true` if the addition overflowed, `false` otherwise */ __always_inline bool imul_ll_overflow(signed long long *result, signed int a, signed int b) { return __builtin_smulll_overflow(a, b, result); } /** * @brief Multiply the two `unsigned int` arguments `a` and `b`, * store the result, and return whether the operation overflowed. * * @param result Where to store the result * @param a First factor * @param b Second factor * @returns `true` if the addition overflowed, `false` otherwise */ __always_inline bool umul_overflow(unsigned int *result, unsigned int a, unsigned int b) { return __builtin_umul_overflow(a, b, result); } /** * @brief Multiply the two `unsigned long` arguments `a` and `b`, * store the result, and return whether the operation overflowed. * * @param result Where to store the result * @param a First factor * @param b Second factor * @returns `true` if the addition overflowed, `false` otherwise */ __always_inline bool umul_l_overflow(unsigned long *result, unsigned long a, unsigned long b) { return __builtin_umull_overflow(a, b, result); } /** * @brief Multiply the two `unsigned long long` arguments `a` and `b`, * store the result, and return whether the operation overflowed. * * @param result Where to store the result * @param a First factor * @param b Second factor * @returns `true` if the addition overflowed, `false` otherwise */ __always_inline bool umul_ll_overflow(unsigned long long *result, unsigned long long a, unsigned long long b) { return __builtin_umulll_overflow(a, b, result); } /** * @brief Test whether the product of `a` and `b` would overflow for * a given `type` of result operand. * * @param type Type that would store the result * @param a First factor * @param b Second factor * @returns `true` if the sum would **not** fit inside `type`, `false` otherwise */ #define mul_test_overflow(type, a, b) __builtin_mul_p(a, b, type) /** @} */