You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

341 lines
10 KiB
C

/* Copyright (C) 2021,2022 fef <owo@fef.moe>. All rights reserved. */
#pragma once
#ifndef __clang__
#error "Unsupported compiler, please use clang"
#endif
#include <gay/cdefs.h>
#include <gay/types.h>
/**
* @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)
/** @} */