diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index bfc718ee..55e704b5 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -35,6 +35,7 @@ add_subdirectory(matrix) add_subdirectory(mpi) add_subdirectory(notifier) add_subdirectory(once) +add_subdirectory(overflow) add_subdirectory(prandom) add_subdirectory(radix) add_subdirectory(ratelimit) diff --git a/examples/overflow/.gitignore b/examples/overflow/.gitignore new file mode 100644 index 00000000..c3333182 --- /dev/null +++ b/examples/overflow/.gitignore @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +/overflow-simple diff --git a/examples/overflow/CMakeLists.txt b/examples/overflow/CMakeLists.txt new file mode 100644 index 00000000..20244570 --- /dev/null +++ b/examples/overflow/CMakeLists.txt @@ -0,0 +1,22 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Copyright(c) 2023 ffashion +# + +add_executable(overflow-simple simple.c) +target_link_libraries(overflow-simple bfdev) +add_test(overflow-simple overflow-simple) + +if(${CMAKE_PROJECT_NAME} STREQUAL "bfdev") + install(FILES + simple.c + DESTINATION + ${CMAKE_INSTALL_DOCDIR}/examples/overflow + ) + + install(TARGETS + overflow-simple + DESTINATION + ${CMAKE_INSTALL_DOCDIR}/bin + ) +endif() diff --git a/examples/overflow/simple.c b/examples/overflow/simple.c new file mode 100644 index 00000000..bf82fee0 --- /dev/null +++ b/examples/overflow/simple.c @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright(c) 2023 John Sanpe + */ + +#define MODULE_NAME "overflow-simple" +#define bfdev_log_fmt(fmt) MODULE_NAME ": " fmt + +#include +#include + +int +main(int argc, const char *argv[]) +{ + bfdev_log_info("(signed char)127 + 1 = %d\n", + bfdev_overflow_add((signed char)127, 1)); + + bfdev_log_info("(unsigned char)255 + 1 = %d\n", + bfdev_overflow_add((unsigned char)255, 1)); + + bfdev_log_info("(signed char)-128 - 1 = %d\n", + bfdev_overflow_sub((signed char)-128, 1)); + + bfdev_log_info("(unsigned char)0 - 1 = %d\n", + bfdev_overflow_sub((unsigned char)0, 1)); + + return 0; +} diff --git a/include/bfdev/compiler.h b/include/bfdev/compiler.h index 8c4bed0e..e227aa51 100644 --- a/include/bfdev/compiler.h +++ b/include/bfdev/compiler.h @@ -38,6 +38,15 @@ BFDEV_BEGIN_DECLS #define bfdev_is_signed(type) (((type)(-1)) < (type)1) #define bfdev_is_unsigned(type) (!bfdev_is_signed(type)) +#define bfdev_type_half_max(type) \ + ((type)1 << (8 * sizeof(type) - 1 - bfdev_is_signed(type))) + +#define bfdev_type_max(type) \ + ((type)((bfdev_type_half_max(type) - 1) + bfdev_type_half_max(type))) + +#define bfdev_type_min(type) \ + (((type)((type)-bfdev_type_max(type) - (type)1))) + /* Not-quite-unique ID. */ #ifndef __BFDEV_UNIQUE_ID # define __BFDEV_UNIQUE_ID(prefix) __BFDEV_PASTE(__BFDEV_PASTE(__UNIQUE_ID_, prefix), __LINE__) diff --git a/include/bfdev/overflow.h b/include/bfdev/overflow.h index 3523b00b..fca71b25 100644 --- a/include/bfdev/overflow.h +++ b/include/bfdev/overflow.h @@ -9,6 +9,7 @@ #include #include #include +#include BFDEV_BEGIN_DECLS @@ -18,7 +19,7 @@ BFDEV_BEGIN_DECLS * @b: second addend. * @d: pointer to store sum. * - * Returns 0 on success. + * Returns true on wrap-around, false otherwise. */ #define bfdev_overflow_check_add(a, b, d) \ bfdev_overflow_check(({ \ @@ -36,7 +37,7 @@ bfdev_overflow_check(({ \ * @b: subtrahend; value to subtract from @a. * @d: pointer to store difference. * - * Returns 0 on success. + * Returns true on wrap-around, false otherwise. */ #define bfdev_overflow_check_sub(a, b, d) \ bfdev_overflow_check(({ \ @@ -54,7 +55,7 @@ bfdev_overflow_check(({ \ * @b: second factor. * @d: pointer to store product. * - * Returns 0 on success. + * Returns true on wrap-around, false otherwise. */ #define bfdev_overflow_check_mul(a, b, d) \ bfdev_overflow_check(({ \ @@ -71,7 +72,7 @@ bfdev_overflow_check(({ \ type __b = (type)(b); \ type __d; \ bfdev_overflow_check_add(__a, __b, &__d) \ - ? (type)~0ULL : __d; \ + ? bfdev_type_max(type) : __d; \ }) #define bfdev_overflow_sub_type(type, a, b) ({ \ @@ -79,7 +80,7 @@ bfdev_overflow_check(({ \ type __b = (type)(b); \ type __d; \ bfdev_overflow_check_sub(__a, __b, &__d) \ - ? (type)~0ULL : __d; \ + ? bfdev_type_min(type) : __d; \ }) #define bfdev_overflow_mul_type(type, a, b) ({ \ @@ -87,7 +88,7 @@ bfdev_overflow_check(({ \ type __b = (type)(b); \ type __d; \ bfdev_overflow_check_mul(__a, __b, &__d) \ - ? (type)~0ULL : __d; \ + ? bfdev_type_max(type) : __d; \ }) static inline __bfdev_must_check bfdev_bool @@ -101,7 +102,8 @@ bfdev_overflow_check(bfdev_bool overflow) * @a: first addend. * @b: second addend. * - * Returns (type)~0ULL on failed. + * Returns: calculate @a + @b, any overflow causing the + * return value to be bfdev_type_max(type). */ #define bfdev_overflow_add(a, b) \ bfdev_overflow_add_type(typeof(a), a, b) @@ -111,7 +113,8 @@ bfdev_overflow_check(bfdev_bool overflow) * @a: minuend; value to subtract from. * @b: subtrahend; value to subtract from @a. * - * Returns (type)~0ULL on failed. + * Returns: calculate @a - @b, any overflow causing the + * return value to be bfdev_type_min(type). */ #define bfdev_overflow_sub(a, b) \ bfdev_overflow_sub_type(typeof(a), a, b) @@ -121,7 +124,8 @@ bfdev_overflow_check(bfdev_bool overflow) * @a: first factor. * @b: second factor. * - * Returns (type)~0ULL on failed. + * Returns: calculate @a * @b, any overflow causing the + * return value to be bfdev_type_max(type). */ #define bfdev_overflow_mul(a, b) \ bfdev_overflow_mul_type(typeof(a), a, b) diff --git a/testsuite/CMakeLists.txt b/testsuite/CMakeLists.txt index 59eefa58..64232d89 100644 --- a/testsuite/CMakeLists.txt +++ b/testsuite/CMakeLists.txt @@ -15,4 +15,5 @@ add_subdirectory(libc) add_subdirectory(list) add_subdirectory(memalloc) add_subdirectory(mpi) +add_subdirectory(overflow) add_subdirectory(slist) diff --git a/testsuite/overflow/.gitignore b/testsuite/overflow/.gitignore new file mode 100644 index 00000000..bc712bcc --- /dev/null +++ b/testsuite/overflow/.gitignore @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +/overflow-selftest diff --git a/testsuite/overflow/CMakeLists.txt b/testsuite/overflow/CMakeLists.txt new file mode 100644 index 00000000..df911529 --- /dev/null +++ b/testsuite/overflow/CMakeLists.txt @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Copyright(c) 2025 John Sanpe +# + +add_executable(overflow-selftest selftest.c) +target_link_libraries(overflow-selftest bfdev testsuite) +add_test(overflow-selftest overflow-selftest) + +if(${CMAKE_PROJECT_NAME} STREQUAL "bfdev") + install(TARGETS + overflow-selftest + DESTINATION + ${CMAKE_INSTALL_DOCDIR}/testsuite + ) +endif() diff --git a/testsuite/overflow/selftest.c b/testsuite/overflow/selftest.c new file mode 100644 index 00000000..b076dd24 --- /dev/null +++ b/testsuite/overflow/selftest.c @@ -0,0 +1,59 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright(c) 2023 John Sanpe + */ + +#define MODULE_NAME "overflow-simple" +#define bfdev_log_fmt(fmt) MODULE_NAME ": " fmt + +#include +#include +#include +#include +#include +#include + +#define TEST(calculate, limit) ({ \ + bool cond; \ + cond = bfdev_overflow_##calculate((limit), 1) == (limit); \ + if (!cond) \ + failed = true; \ + cond ? "okay" : "failed"; \ +}) + +TESTSUITE( + "overflow:selftest", NULL, NULL, + "overflow selftest" +) { + bool failed; + + failed = false; + bfdev_log_info("add char: %s\n", TEST(add, (signed char)SCHAR_MAX)); + bfdev_log_info("add short: %s\n", TEST(add, (signed short)SHRT_MAX)); + bfdev_log_info("add int: %s\n", TEST(add, (signed int)INT_MAX)); + bfdev_log_info("add long: %s\n", TEST(add, (signed long)LONG_MAX)); + bfdev_log_info("add long long: %s\n", TEST(add, (signed long long)LLONG_MAX)); + + bfdev_log_info("add unsigned char: %s\n", TEST(add, (unsigned char)UCHAR_MAX)); + bfdev_log_info("add unsigned short: %s\n", TEST(add, (unsigned short)USHRT_MAX)); + bfdev_log_info("add unsigned int: %s\n", TEST(add, (unsigned int)UINT_MAX)); + bfdev_log_info("add unsigned long: %s\n", TEST(add, (unsigned long)ULONG_MAX)); + bfdev_log_info("add unsigned long long: %s\n", TEST(add, (unsigned long long)ULLONG_MAX)); + + bfdev_log_info("sub char: %s\n", TEST(sub, (signed char)SCHAR_MIN)); + bfdev_log_info("sub short: %s\n", TEST(sub, (signed short)SHRT_MIN)); + bfdev_log_info("sub int: %s\n", TEST(sub, (signed int)INT_MIN)); + bfdev_log_info("sub long: %s\n", TEST(sub, (signed long)LONG_MIN)); + bfdev_log_info("sub long long: %s\n", TEST(sub, (signed long long)LLONG_MIN)); + + bfdev_log_info("sub unsigned char: %s\n", TEST(sub, (unsigned char)0)); + bfdev_log_info("sub unsigned short: %s\n", TEST(sub, (unsigned short)0)); + bfdev_log_info("sub unsigned int: %s\n", TEST(sub, (unsigned int)0)); + bfdev_log_info("sub unsigned long: %s\n", TEST(sub, (unsigned long)0)); + bfdev_log_info("sub unsigned long long: %s\n", TEST(sub, (unsigned long long)0)); + + if (failed) + return -BFDEV_EFAULT; + + return -BFDEV_ENOERR; +}