From 20a9ef41663ca45fc3ddf4f229b63d59faeb27bb Mon Sep 17 00:00:00 2001 From: yyc12345 Date: Mon, 11 Aug 2025 21:57:42 +0800 Subject: [PATCH] fix: fix comment for new added files. - translate zh-CN comment into en-US. - change some comment into Doxygen style. - add lost Doxygen comment. - enrich the testbench for ceil_div. - add lost metaprogramming functions for some files in macro namespace. --- src/yycc/flag_enum.hpp | 5 +- src/yycc/macro/ptr_size_detector.hpp | 22 ++ src/yycc/macro/stl_detector.hpp | 25 ++ src/yycc/num/op.hpp | 31 ++- src/yycc/num/safe_cast.hpp | 118 ++++---- src/yycc/num/safe_op.hpp | 396 +++++++++++++++++++-------- src/yycc/string/op.cpp | 1 - testbench/yycc/num/op.cpp | 8 +- 8 files changed, 419 insertions(+), 187 deletions(-) diff --git a/src/yycc/flag_enum.hpp b/src/yycc/flag_enum.hpp index 4d3ca89..5edb045 100644 --- a/src/yycc/flag_enum.hpp +++ b/src/yycc/flag_enum.hpp @@ -10,12 +10,11 @@ */ namespace yycc::flag_enum { - // YYC MARK: - // // Reference: // Enum operator overload: https://stackoverflow.com/a/71107019 // Constexpr operator overload: https://stackoverflow.com/a/17746099 - // + + // YYC MARK: // Currently, the solution of "Constexpr operator overload" is not used. // We use explicit way, "Enum operator overload". diff --git a/src/yycc/macro/ptr_size_detector.hpp b/src/yycc/macro/ptr_size_detector.hpp index 15fad21..b79dcee 100644 --- a/src/yycc/macro/ptr_size_detector.hpp +++ b/src/yycc/macro/ptr_size_detector.hpp @@ -4,3 +4,25 @@ #if (defined(YYCC_PTRSIZE_32) + defined(YYCC_PTRSIZE_64)) != 1 #error "Current environment used pointer size is not supported!" #endif + +namespace yycc::macro::ptr_size { + + /// @brief The pointer size kind. + enum class PtrSizeKind { + Bits32, ///< 32-bit environment + Bits64 ///< 64-bit environment + }; + + /** + * @brief Fetch the pointer size + * @return The kind of pointer size. + */ + inline constexpr PtrSizeKind get_ptr_size() { +#if defined(YYCC_PTRSIZE_32) + return PtrSizeKind::Bits32; +#else + return PtrSizeKind::Bits64; +#endif + } + +} // namespace yycc::macro::ptr_size diff --git a/src/yycc/macro/stl_detector.hpp b/src/yycc/macro/stl_detector.hpp index 100333f..cfddda9 100644 --- a/src/yycc/macro/stl_detector.hpp +++ b/src/yycc/macro/stl_detector.hpp @@ -12,3 +12,28 @@ #else #error "Current STL is not supported!" #endif + +namespace yycc::macro::stl { + + /// @brief The STL implementation kind. + enum class StlKind { + MSSTL, ///< Microsoft STL + GNUSTL, ///< GNU STL + CLANGSTL ///< Clang STL + }; + + /** + * @brief Fetch the STL implementation + * @return The kind of STL implementation. + */ + inline constexpr StlKind get_stl() { +#if defined(YYCC_STL_MSSTL) + return StlKind::MSSTL; +#elif defined(YYCC_STL_GNUSTL) + return StlKind::GNUSTL; +#else + return StlKind::CLANGSTL; +#endif + } + +} // namespace yycc::macro::stl diff --git a/src/yycc/num/op.hpp b/src/yycc/num/op.hpp index 4d5f1d2..47a6b69 100644 --- a/src/yycc/num/op.hpp +++ b/src/yycc/num/op.hpp @@ -3,32 +3,35 @@ #include /** - * @brief The namespace providing function relative robust numeric operations. + * @brief The namespace providing functions for robust numeric operations. * @details - * 在使用Rust编写一些程序后,深刻地认识到Rust中针对基本类型的运算符的丰富程度。 - * 提供了诸如整数向上取整除法等便利操作。 - * 因此我在这个命名空间里复刻Rust中的这些便利功能。 + * After writing some programs in Rust, I deeply appreciated the richness of operators + * for primitive types in Rust, which provides convenient operations like ceiling integral division. + * Therefore, I replicate these convenient features from Rust in this namespace. * - * 由于没有需求,目前暂未实现以下功能: - * \li 仅支持无符号整数向上取整除法。 - * \li 显式指定运算溢出行为 + * Currently unimplemented features due to lack of demand: + * \li Only supports unsigned integer ceiling division */ namespace yycc::num::op { /** - * @brief 无符号整数的向上整除 + * @brief Unsigned integer ceiling division * @details - * 执行两个无符号整数之间的整除,并将结果向上取整。 - * 该函数可能在很多加密函数中进行使用以分配合适空间。 - * @tparam T 除法操作建立在的无符号整数类型 - * @param[in] lhs 左操作数 - * @param[in] rhs 右操作数 - * @return 向上取整的整除结果 + * Performs division between two unsigned integers and rounds up the result. + * @exception std::logic_error If the divisor is zero + * @tparam T The unsigned integer type for division operation + * @param[in] lhs Left operand + * @param[in] rhs Right operand + * @return Ceiling division result */ template requires std::unsigned_integral T div_ceil(T lhs, T rhs) { + // Check divisor first if (rhs == 0) throw std::logic_error("div with 0"); + // YYC MARK: + // We use this algorithm, instead of traditional `(lhs + rhs - 1) / rhs`, + // which may have unsafe overflow case. return (lhs % rhs == 0) ? (lhs / rhs) : (lhs / rhs) + 1u; } diff --git a/src/yycc/num/safe_cast.hpp b/src/yycc/num/safe_cast.hpp index 85fc44d..59f641b 100644 --- a/src/yycc/num/safe_cast.hpp +++ b/src/yycc/num/safe_cast.hpp @@ -7,41 +7,50 @@ /** * @brief The namespace providing functions which safely cast numeric value from one type to another. * @details - * 在编写Rust的时候,深刻地认识到,在范围不同的类型之间进行转换是一件非常重要且容易出错的事情。 - * 对于拓宽转换,我们可以安全地,不加考虑地直接进行转换。 - * 对于收窄转换,我们则需要引入Result机制,来判断是否出错。 - * 这些功能在Rust中对所有基础类型都有实现,且通过From trait来进行统一管理,非常完美。 - * 但在C++中,我们需要手动重现它们。 + * When writing Rust code, I deeply realized that casting between types with different ranges is very important, + * but it is greatly easy to make mistake which finally cause fatal error. + * For widening conversions, we can safely perform them directly without consideration. + * For narrowing conversions, we need to introduce a Result mechanism to determine if errors occur. + * These features are implemented for all primitive types in Rust, + * and managed uniformly through the \c From and \c TryFrom trait, which is perfect. + * But in C++, we need to manually replicate them. * - * 在这个命名空间中,我们将转换函数分为了两类,一种是to,适用于肯定能安全转换的类型。 - * 另一种是try_to,用于可能存在风险的转换。 - * 但直接套用CAN_SAFE_TO来决定是否可以安全转换是不正确的。 - * Rust中,这些转换安全与否是通过trait的不同(From和TryFrom)来手动编写的。 - * 而在这里我们粗暴地用编译时数据类型大小的比较结果来决定是否安全转换。 - * 这就导致那些变长基本数据类型在不同编译平台上致使CAN_SAFE_TO输出不同结果, - * 从而影响我们的代码的可移植性。 - * 例如在uint64_t和size_t之间的变换,在64位平台上我们可以使用to函数直接转换, - * 然而在32位平台上,就会出现编译报错,造成了代码的可移植性问题。 - * Rust中的解决方案是Rust规定了Usize的最小大小(32位),只有小于其最小大小的数据才能安全的转换(例如u16)。 - * 但在C++中我们不能那么做,也无从做起,因为我们不能知道每一个变长基本数据类型的最小大小。 - * 所以我们使用另一种方案来解决这个问题。 + * In this namespace, we divide conversion functions into two categories: + * \li \c to for definitely safe conversions, + * \li \c try_to for potentially risky conversions. + * There is a metaprogramming concept CAN_SAFE_TO\ to determine if a conversion is safe, + * which applied for these functions as the constraint. + * + * However, directly using \c CAN_SAFE_TO to determine whether the convertion is safe is incorrect. + * In Rust, whether these conversions are safe is manually determined by different traits ( \c From and \c TryFrom). + * But in C++, we brutally use the size of data types in compile-time to determine safe conversion, + * which causes variable-length data types produces different \c CAN_SAFE_TO results on different platforms, + * which affecting our code's portability. + * For example, we can use 'to' directly for conversion between \c uint64_t and \c size_t: on 64-bit platforms, + * but on 32-bit platforms it causes compile-time errors, resulting in portability issues. + * + * Rust's solution is to define the minimum size of \c usize (32-bit), + * allowing safe conversion only for data smaller than this (e.g. \c u16 ). + * But in C++ we can't do this because we can't know the minimum size of every variable-length primitive data type. + * So we use another solution. * - * 我们的解决方案是,为to函数强制应用CAN_SAFE_TO规则,而不对try_to函数强制应用。 - * 在try_to函数内部,我们再使用CAN_SAFE_TO,判断是否可以安全转换,如果可以,就直接转换,否则做一系列判定。 - * 这样以来,程序员就需要手动判定两个数据类型之间是否肯定可以安全转换,然后再使用to函数。 - * 但至少,编译器会在你误使用to函数的时候,及时地给你抛出错误,这时候只要改用try_to就好了。 - * 同时,如果全篇使用try_to,也不会因为做了无用检查而影响性能,因为无用检查已经通过if constexpr删除了。 + * Our solution is to enforce \c CAN_SAFE_TO rules for \c to functions but not for \c try_to functions. + * Inside \c try_to, we use \c CAN_SAFE_TO to determine if safe conversion is possible. + * If yes, convert directly, otherwise perform essential checks before convertion. + * So under this solution, programmers need manually determine if conversion between two types is definitely safe before using \c to . + * But at least, the compiler will throw errors when \c to is inviable, and the only thing you should do is switching to \c try_to. + * Also, using \c try_to everywhere won't impact performance as unnecessary checks are removed via if constexpr statement in function. * - * 由于没有需求,目前不支持以下转换: - * \li 浮点数与浮点数之间的转换。 - * \li 浮点数与整数之间的转换。 + * Currently unsupported conversions due to lack of demand: + * \li Floating-point to floating-point conversions + * \li Floating-point to integer conversions */ namespace yycc::num::safe_cast { /// @brief All possible error raised in this module. enum class CastError { - Overflow, ///< 转换时发生向上溢出错误。 - Underflow, ///< 转换时发生向下溢出错误。 + Overflow, ///< Overflow error occurred during conversion. + Underflow, ///< Underflow error occurred during conversion. }; /// @brief The result type in this module. @@ -50,17 +59,17 @@ namespace yycc::num::safe_cast { /** * @private - * @brief 检查一个整数类型是否可以安全地转换为另一个整数类型 - * @return + * @brief Check if an integer type can be safely converted to another integer type + * @return True if it can be safely converted, false otherwise. */ template requires std::integral && std::integral constexpr bool can_safe_to() { - // 检查 TSrc 和 TDst 是否有符号 + // Fetch the sign info of TSrc and TDst. constexpr bool is_src_signed = std::is_signed_v; constexpr bool is_dst_signed = std::is_signed_v; - // 获取 TSrc 和 TDst 的范围 + // Get the range of TSrc and TDst. constexpr TSrc src_min = std::numeric_limits::min(); constexpr TSrc src_max = std::numeric_limits::max(); constexpr TDst dst_min = std::numeric_limits::min(); @@ -68,21 +77,22 @@ namespace yycc::num::safe_cast { if constexpr (is_src_signed) { if constexpr (is_dst_signed) { - // 有符号向有符号转换,两端都需要检查。 - // 如果完全处于范围内,则肯定可以安全转换。 + // Signed to signed conversion, both upper and lower bound need to be checked. + // If completely within the range, it is definitely safe. return dst_min <= src_min && dst_max >= src_max; } else { - // 有符号向无符号转换,总是不安全的。 - // 因为会存在负数情况 + // Signed to unsigned conversion, always unsafe. + // Because negative numbers exist. return false; } } else { if constexpr (is_dst_signed) { - // 无符号向有符号转换,则只检查上端。 - // 如果上端够小内,则肯定可以安全转换。 + // Unsigned to signed conversion, only check the upper bound. + // If the upper bound is small enough, it is definitely safe. return dst_max >= src_max; } else { - // 无符号向无符号转换,则只检查上端,因为下端均为0。 + // Unsigned to unsigned conversion, only check the upper bound, + // because the lower bound is 0. return dst_max >= src_max; } } @@ -90,17 +100,17 @@ namespace yycc::num::safe_cast { /** * @private - * @brief can_safe_to()的变量版本 - * @details 为后文约束提供的便利变量 + * @brief Variable version of can_safe_to() + * @details Convenience variable for subsequent constraints */ template requires std::integral && std::integral inline constexpr bool CAN_SAFE_TO = can_safe_to(); /** - * @brief 将一个整数类型转换为另一个整数类型。 - * @details 类似于Rust中的From<> trait,只不过颠倒了一下顺序(从from变为to)。 - * @return 转换后的结果。 + * @brief Convert an integer type to another integer type. + * @details Similar to Rust's \c From trait, but with reversed direction (from "from" to "to"). + * @return The converted result. */ template requires std::integral && std::integral && CAN_SAFE_TO @@ -109,42 +119,42 @@ namespace yycc::num::safe_cast { } /** - * @brief 尝试将一个整数类型转换为另一个整数类型。 - * @details 类似于Rust中的TryFrom<> trait,只不过颠倒了一下顺序(从from变为to)。 - * @return 一个包含转换结果的Result类型。 + * @brief Attempt to convert an integer type to another integer type. + * @details Similar to Rust's \c TryFrom trait, but with reversed direction (from "from" to "to"). + * @return A result containing the conversion result or convertion error. */ template requires std::integral && std::integral Result try_to(const TSrc& lhs) { - // 检查是否可以直接转换 + // Check whether we can convert directly. if constexpr (CAN_SAFE_TO) { return static_cast(lhs); } else { - // 检查 TSrc 和 TDst 是否有符号 + // Fetch the sign info of TSrc and TDst. constexpr bool is_src_signed = std::is_signed_v; constexpr bool is_dst_signed = std::is_signed_v; - // 获取 TSrc 和 TDst 的范围 + // Fetch the range of TSrc and TDst. constexpr TSrc src_min = std::numeric_limits::min(); constexpr TSrc src_max = std::numeric_limits::max(); constexpr TDst dst_min = std::numeric_limits::min(); constexpr TDst dst_max = std::numeric_limits::max(); - // 检查是否可以安全转换 + // Check whether we can convert safely. if constexpr (is_src_signed == is_dst_signed) { - // 如果两者都是有符号或无符号,直接比较范围 + // If both are signed or unsigned, compare ranges directly. if (lhs < dst_min) return std::unexpected(CastError::Underflow); if (lhs > dst_max) return std::unexpected(CastError::Overflow); return static_cast(lhs); } else { - // 两者符号不一致 + // If signs are different, we need to check the value range. if constexpr (is_src_signed) { - // 如果 TSrc 是有符号,TDst 是无符号,需要确保 lhs 不小于 0 + // If TSrc is signed, TDst is unsigned, we need to ensure lhs is not negative. if (lhs < 0) return std::unexpected(CastError::Underflow); if (lhs > dst_max) return std::unexpected(CastError::Overflow); return static_cast(lhs); } else { - // 如果 TSrc 是无符号,TDst 是有符号,需要确保 lhs 不大于 TDst 的最大值 + // If TSrc is unsigned, TDst is signed, we need to ensure lhs is not greater than dst_max. if (lhs > dst_max) return std::unexpected(CastError::Overflow); return static_cast(lhs); } diff --git a/src/yycc/num/safe_op.hpp b/src/yycc/num/safe_op.hpp index 2c0ff94..e68f386 100644 --- a/src/yycc/num/safe_op.hpp +++ b/src/yycc/num/safe_op.hpp @@ -24,54 +24,68 @@ /** * @brief The namespace providing Rust-like safe arithmetic operations. + * @details + * After writing some programs in Rust, I've deeply realized the richness of operators for primitive types in Rust. + * You can explicitly specify the behavior of arithmetic overflow + * (choose one of wrapping, checked, overflowing, and saturating). + * Therefore, I'm replicating these convenient features from Rust in this namespace. + * + * Additionally, I provide a bunch of extra operations, called ordinary operation. + * These functions are just an alias to wrapping operator, indicating wrapping is the normal case. + * These normal operators will not have any undefined behavior which C++ will make. + * It basically like \c Add, \c Sub, \c Mul, and \c Div traits in Rust, + * providing safe operations for primitive types. */ namespace yycc::num::safe_op { - // YYC MARK: - // 在使用Rust编写一些程序后,深刻地认识到Rust中针对基本类型的运算符的丰富程度。 - // 可以显式地指定运算溢出的行为(wrapping,checked,overflowing,saturating四选一) - // 因此我在这个命名空间里复刻Rust中的这些便利功能。 + /* + Implementation notes: - // YYC MARK: - // 实现说明: - // * Wrapping 运算: - // - 无符号整数直接使用默认溢出行为。 - // - 有符号整数通过无符号类型转换模拟溢出回绕。 - // * Checked 运算: - // - 使用编译器内置函数检测溢出,返回std::optional。 - // - 除零或溢出时返回std::nullopt。 - // * Overflowing 运算: - // - 返回包含结果和溢出标志的结构体OverflowResult。 - // - 显式处理除零和有符号最小值除以-1的情况。 - // * Saturating 运算: - // - 溢出时返回类型最大值或最小值。 - // - 根据操作数符号判断饱和方向。 + - Wrapping operation: + - Unsigned integer use default overflow behavior. + - Signed integer will be casted into unsigned integer before operation to simulate wrapping overflow. + - Checked operation: + - Use compiler built-in function to detect overflow, return std::optional. + - Return std::nullopt when division by zero or overflow. + - Overflowing operation: + - Return std::pair where bool indicates whether overflow occurs. + - Explicitly handle 2 division undefined behavior: + - Division by zero. + - Signed minimum value divided by -1. + - Saturating operation: + - Return maximum or minimum value when overflow or underflow occurs. + - Determine saturation direction (overflow or underflow) based on operand sign. + */ #pragma region Undefined Behaviors - // YYC MARK: - // 需要注意以下未定义行为: - // * 有符号整数运算结果超出该类型表示范围时(如 INT_MAX + 1),行为未定义。 - // * 对 INT_MIN / -1 的除法可能溢出,导致UB。 - // * 无符号整数溢出时行为是明确定义的(按模 2^N 回绕),但需注意逻辑错误。 - // * 除数为零时行为未定义。 + /* + YYC MARK: + Following undefined behaviors should be noticed: + - Signed integer overflow and underflow (e.g. INT_MAX + 1). + - Perform `INT_MIN / -1` division. + - The right operand in division is zero. + */ /** * @private - * @brief 两数相加,同时考虑有符号整数溢出的未定义行为 - * @param a 加法的左操作数 - * @param b 加法的右操作数 - * @return 考虑了有符号整数未定义行为的加法结果。 + * @brief Adds two numbers while considering the undefined behavior of signed integer overflow. + * @tparam T Integer type + * @param[in] a The left operand of the addition + * @param[in] b The right operand of the addition + * @return The result of the addition that takes into account the undefined behavior of signed integers. */ template requires std::integral T ub_signed_int_add(T a, T b) { if constexpr (std::is_unsigned_v) { - // 无符号数的加减乘都是自然回绕的,可以直接使用运算符。 + // Add, Sub, Mul and Div for unsigned integer is natural wrapping. + // So we can use operator simply and directly. return a + b; } else { - // 有符号数的溢出在C++中是未定义的。 - // 所以需要使用位操作强制转换为无符号进行计算,然后再转回来。 + // In C++, it is undefined behavior that signed integer overflow. + // So we need cast them into unsigned integer forcely before operation, + // do operation for them, and cast the result back to simulate wrapping overflow. using UT = std::make_unsigned_t; return static_cast(static_cast(a) + static_cast(b)); } @@ -79,10 +93,11 @@ namespace yycc::num::safe_op { /** * @private - * @brief 两数相减,同时考虑有符号整数溢出的未定义行为 - * @param a 减法的左操作数 - * @param b 减法的右操作数 - * @return 考虑了有符号整数未定义行为的减法结果。 + * @brief Subtracts two numbers while considering the undefined behavior of signed integer overflow. + * @tparam T Integer type + * @param[in] a The left operand of the subtraction + * @param[in] b The right operand of the subtraction + * @return The result of the subtraction that takes into account the undefined behavior of signed integers. */ template requires std::integral @@ -97,10 +112,11 @@ namespace yycc::num::safe_op { /** * @private - * @brief 两数相乘,同时考虑有符号整数溢出的未定义行为 - * @param a 乘法的左操作数 - * @param b 乘法的右操作数 - * @return 考虑了有符号整数未定义行为的乘法结果。 + * @brief Multiplies two numbers while considering the undefined behavior of signed integer overflow. + * @tparam T Integer type + * @param[in] a The left operand of the multiplication + * @param[in] b The right operand of the multiplication + * @return The result of the multiplication that takes into account the undefined behavior of signed integers. */ template requires std::integral @@ -115,17 +131,19 @@ namespace yycc::num::safe_op { /** * @private - * @brief 检查有符号最小值除以-1时的未定义行为 - * @param a 除法的左操作数 - * @param b 除法的右操作数 - * @return 如果会发生未定义行为,则为true,否则为false + * @brief Checks for undefined behavior when dividing the minimum signed integer value by -1. + * @tparam T Integer type + * @param[in] a The left operand of the division + * @param[in] b The right operand of the division + * @return Returns true if undefined behavior will occur, false otherwise. */ template requires std::integral bool ub_signed_int_min_div_minus_one(T a, T b) { if constexpr (std::is_signed_v) { - // 对有符号数来说, INT_MIN / -1 的除法可能溢出 - // (如 INT_MIN == -INT_MIN - 1 时),导致未定义行为。 + // For signed value, `INT_MIN / -1` may cause overflow, + // which finally cause the undefined behavior, + // due to the truth that `INT_MIN == -INT_MIN - 1`. if (b == -1 && a == std::numeric_limits::min()) { return true; } else { @@ -138,11 +156,11 @@ namespace yycc::num::safe_op { /** * @private - * @brief 检查除以0的未定义行为 - * @details 如果发生除以0,则引发panic - * @param a 除法的左操作数 - * @param b 除法的右操作数 - * @return 如果会发生未定义行为,则为true,否则为false + * @brief Checks for the undefined behavior of division by zero. + * @tparam T Integer type + * @param[in] a The left operand of the division + * @param[in] b The right operand of the division + * @return Returns true if undefined behavior will occur, false otherwise. */ template requires std::integral @@ -155,18 +173,19 @@ namespace yycc::num::safe_op { #pragma region Hardware Operation Overflow // YYC MARK: -// 定义一个方便在Windows函数中计算的宏 +// If we are using Windows function family, +// we define a convenient macro assisting overflow calculation. #if defined(YYCC_HARDWARE_OVERFLOW_WIN32_FNS) #define WIN_EASY_OPER(fn, ty, a, b, c) FAILED(fn(static_cast(a), static_cast(b), reinterpret_cast(c))) #endif /** * @private - * @brief 基于硬件指令的带溢出检测的加法 - * @param[in] a 加法的左操作数 - * @param[in] b 加法的右操作数 - * @param[out] c 存放结果的引用 - * @return 如果发生溢出,则为true,否则为false。 + * @brief Addition with overflow detection based on hardware instructions. + * @param[in] a The left operand of the addition. + * @param[in] b The right operand of the addition. + * @param[out] c The pointer to the variable storing result. + * @return Returns true if an overflow occurs, false otherwise. */ template requires std::integral @@ -203,8 +222,9 @@ namespace yycc::num::safe_op { static_assert(std::false_type::value, "not supported integral type."); } } - // 由于Windows函数限制,如果发生溢出,函数不会计算结果。 - // 所以我们要手动填写回绕了的结果 + // Due to the limitation of Windows function family, + // if overflow or underflow occurs, there is no calculation result. + // So we need fill the wrapping value manually. if (overflow) *c = ub_signed_int_add(a, b); return overflow; #endif @@ -212,11 +232,11 @@ namespace yycc::num::safe_op { /** * @private - * @brief 基于硬件指令的带溢出检测的减法 - * @param[in] a 减法的左操作数 - * @param[in] b 减法的右操作数 - * @param[out] c 存放结果的引用 - * @return 如果发生溢出,则为true,否则为false。 + * @brief Subtraction with overflow detection based on hardware instructions. + * @param[in] a The left operand of the subtraction. + * @param[in] b The right operand of the subtraction. + * @param[out] c The pointer to the variable storing the result. + * @return Returns true if an overflow occurs, false otherwise. */ template requires std::integral @@ -253,7 +273,7 @@ namespace yycc::num::safe_op { static_assert(std::false_type::value, "not supported integral type."); } } - // 同理,手算溢出值 + // Similarly, manually calculate wrapping value. if (overflow) *c = ub_signed_int_sub(a, b); return overflow; #endif @@ -261,11 +281,11 @@ namespace yycc::num::safe_op { /** * @private - * @brief 基于硬件指令的带溢出检测的乘法 - * @param[in] a 乘法的左操作数 - * @param[in] b 乘法的右操作数 - * @param[out] c 存放结果的引用 - * @return 如果发生溢出,则为true,否则为false。 + * @brief Multiplication with overflow detection based on hardware instructions. + * @param[in] a The left operand of the multiplication. + * @param[in] b The right operand of the multiplication. + * @param[out] c The reference to the variable storing the result. + * @return Returns true if an overflow occurs, false otherwise. */ template requires std::integral @@ -302,13 +322,14 @@ namespace yycc::num::safe_op { static_assert(std::false_type::value, "not supported integral type."); } } - // 同理,手算溢出值 + // Similarly, manually calculate wrapping value. if (overflow) *c = ub_signed_int_mul(a, b); return overflow; #endif } -// YYC MARK: 删除定义的宏,防止污染 +// YYC MARK: +// Delete the defined macro to prevent polluting the content later. #if defined(YYCC_HARDWARE_OVERFLOW_WIN32_FNS) #undef WIN_EASY_OPER #endif @@ -318,36 +339,64 @@ namespace yycc::num::safe_op { #pragma region Wrapping operations // YYC MARK: - // 使用 wrapping_* 方法在所有模式下进行包裹。 + // wrapping_* function family will wrap the result in any scenario. + /** + * @brief Performs a wrapping addition operation on two integers. + * @tparam T Integer type. + * @param[in] a The left operand of the addition. + * @param[in] b The right operand of the addition. + * @return The wrapping result of the addition operation. + */ template requires std::integral T wrapping_add(T a, T b) { return ub_signed_int_add(a, b); } + /** + * @brief Performs a wrapping subtraction operation on two integers. + * @tparam T Integer type. + * @param[in] a The left operand of the subtraction. + * @param[in] b The right operand of the subtraction. + * @return The wrapping result of the subtraction operation. + */ template requires std::integral T wrapping_sub(T a, T b) { return ub_signed_int_sub(a, b); } + /** + * @brief Performs a wrapping multiplication operation on two integers. + * @tparam T Integer type. + * @param[in] a The left operand of the multiplication. + * @param[in] b The right operand of the multiplication. + * @return The wrapping result of the multiplication operation. + */ template requires std::integral T wrapping_mul(T a, T b) { return ub_signed_int_mul(a, b); } + /** + * @brief Performs a wrapping division operation on two integers. + * @tparam T Integer type. + * @param[in] a The left operand of the division. + * @param[in] b The right operand of the division. + * @return The wrapping result of the division operation. + * @exception std::logic_error If division by zero occurs. + */ template requires std::integral T wrapping_div(T a, T b) { - // 除以0是未定义行为 + // Division by zero is undefined behavior. if (ub_div_zero(a, b)) throw std::logic_error("div with 0"); - // 对有符号数来说, INT_MIN / -1 的除法可能溢出 - // (如 INT_MIN == -INT_MIN - 1 时),导致未定义行为。 + // `INT_MIN / -1` overflow undefined behavior. if (ub_signed_int_min_div_minus_one(a, b)) - // 此时a就是有符号整数最小值,直接返回他即可。 - // 不需要再通过std::numeric_limits去访问最小值。 + // "a" self is the minimum value of signed integer, return it directly. + // There is no need to re-fetch it by std::numeric_limits. return a; return a / b; } @@ -356,8 +405,17 @@ namespace yycc::num::safe_op { #pragma region Checked operations - // YYC MARK: 使用 checked_* 方法时发生溢出,则返回 None 值 + // YYC MARK: + // If overflow occurs when using checked_* function family, + // these functions will return std::nullopt, otherwise the computed result. + /** + * @brief Performs a checked addition operation on two integers. + * @tparam T Integer type. + * @param[in] a The left operand of the addition. + * @param[in] b The right operand of the addition. + * @return An std::optional containing the result if no overflow occurs, otherwise std::nullopt. + */ template requires std::integral std::optional checked_add(T a, T b) { @@ -366,6 +424,13 @@ namespace yycc::num::safe_op { return result; } + /** + * @brief Performs a checked subtraction operation on two integers. + * @tparam T Integer type. + * @param[in] a The left operand of the subtraction. + * @param[in] b The right operand of the subtraction. + * @return An std::optional containing the result if no overflow occurs, otherwise std::nullopt. + */ template requires std::integral std::optional checked_sub(T a, T b) { @@ -374,6 +439,13 @@ namespace yycc::num::safe_op { return result; } + /** + * @brief Performs a checked multiplication operation on two integers. + * @tparam T Integer type. + * @param[in] a The left operand of the multiplication. + * @param[in] b The right operand of the multiplication. + * @return An std::optional containing the result if no overflow occurs, otherwise std::nullopt. + */ template requires std::integral std::optional checked_mul(T a, T b) { @@ -382,12 +454,22 @@ namespace yycc::num::safe_op { return result; } + /** + * @brief Performs a checked division operation on two integers. + * @tparam T Integer type. + * @param[in] a The left operand of the division. + * @param[in] b The right operand of the division. + * @return + * An std::optional containing the result, + * if no undefined behavior (division by zero or overflow) occurs, + * otherwise std::nullopt. + */ template requires std::integral std::optional checked_div(T a, T b) { - // 除以0返回空值 + // Division by zero is undefined behavior. if (ub_div_zero(a, b)) return std::nullopt; - // 溢出返回空值 + // `INT_MIN / -1` overflow undefined behavior. if (ub_signed_int_min_div_minus_one(a, b)) return std::nullopt; return a / b; } @@ -396,13 +478,15 @@ namespace yycc::num::safe_op { #pragma region Overflowing operations - // YYC MARK: 使用 overflowing_* 方法返回该值和一个指示是否存在溢出的布尔值 + // YYC MARK: + // overflowing_* function family return a tuple with 2 items as the result. + // First is the wrapping value and second is a boolean indicates whether overflow occurs. /** - * @brief Overflow系列运算函数返回的结果 + * @brief The result returned by the overflow function family. * @details - * 第一项为运算结果。 - * 第二项为指示是否发生了溢出,true为溢出,否则为false。 + * The first item is the operation result. + * The second item indicates whether an overflow has occurred. true means overflow, otherwise false. */ template requires std::integral @@ -435,11 +519,11 @@ namespace yycc::num::safe_op { template requires std::integral OverflowingPair overflowing_div(T a, T b) { - // 除以0需要panic + // Division by zero is undefined behavior. if (ub_div_zero(a, b)) throw std::logic_error("div with 0"); - // 溢出只可能发生在有符号最小值除以-1上 + // `INT_MIN / -1` overflow undefined behavior. if (ub_signed_int_min_div_minus_one(a, b)) { - // a就是最小值,无需再次获取 + // "a" self is minimum value, no need to get it again. return std::make_pair(a, true); } else { return std::make_pair(a / b, false); @@ -450,25 +534,41 @@ namespace yycc::num::safe_op { #pragma region Saturating operations - // YYC MARK: 使用 saturating_* 方法使值达到最小值或最大值 - // 无符号的溢出判定较为简单,有符号的则稍显复杂,具体遵守如下规则 - // 认识到对于有符号:abs(MIN) = abs(MAX) + 1 - // * 加法: - // - 区间运算[a, b] + [c, d] = [a+c, b+d] - // - 正+正 -> [0, MAX] + [0, MAX] -> [0, 2 * MAX],可能上溢 -> 饱和到max - // - 负+负 -> [MIN, -1] + [MIN, -1] -> [2 * MIN, -2],可能下溢 -> 饱和到min - // - 正+负 -> [0, MAX] + [MIN, -1] -> [MIN, MAX - 1],不可能溢出 - // * 减法: - // - 区间运算[a, b] - [c, d] = [a-d, b-c] - // - 正-负 -> [0, MAX] - [MIN, -1] -> [1, MAX - MIN],可能上溢 -> 饱和到max - // - 负-正 -> [MIN, -1] - [0, MAX] -> [MIN - MAX, -1],可能下溢 -> 饱和到min - // - 正-正 -> [0, MAX] - [0, MAX] -> [-MAX, MAX],不可能溢出 - // - 负-负 -> [MIN, -1] - [MIN, -1] -> [MIN + 1, -(MIN + 1)],不可能溢出 - // * 乘法: - // - 正*正 -> 可能上溢 -> 饱和到max - // - 正*负 -> 可能下溢 -> 饱和到min - // - 负*负 -> 可能上溢 -> 饱和到max + /* + YYC MARK: + The direction of saturation is pretty simple for unsigned integer. + However, it is slightly complex for signed integer. + In detail, it follow following rules: + Acknowledge the truth for signed integer: abs(MIN) = abs(MAX) + 1 + - ADD operation: + - Range operation rule: [a, b] + [c, d] = [a+c, b+d] + - Pos+Pos -> [0, MAX] + [0, MAX] -> [0, 2 * MAX]. May overflow -> Saturating to MAX. + - Neg+Neg -> [MIN, -1] + [MIN, -1] -> [2 * MIN, -2]. May underflow -> Saturating to MIN. + - Pos+Neg -> [0, MAX] + [MIN, -1] -> [MIN, MAX - 1]. Impossible overflow or underflow. + - SUB Operation: + - Range operation rule: [a, b] - [c, d] = [a-d, b-c] + - Pos-Neg -> [0, MAX] - [MIN, -1] -> [1, MAX - MIN]. Maybe overflow -> Saturating to MAX. + - Neg-Pos -> [MIN, -1] - [0, MAX] -> [MIN - MAX, -1]. Maybe underflow -> Saturating to MIN. + - Pos-Pos -> [0, MAX] - [0, MAX] -> [-MAX, MAX]. Impossible overflow or underflow. + - Neg-Neg -> [MIN, -1] - [MIN, -1] -> [MIN + 1, -(MIN + 1)]. Impossible overflow or underflow. + - MUL Operation: + - Pos*Pos -> Maybe overflow -> Saturating to MAX. + - Pos*Neg -> Maybe underflow -> Saturating to MIN. + - Neg*Neg -> Maybe overflow -> Saturating to MAX. + */ + + /** + * @brief Performs a saturating addition operation on two integers. + * @details + * If an overflow occurs during the addition, + * the result will be saturated to the maximum/minimum value, + * according to the the direction of overflow (overflow or underflow). + * @tparam T Integer type. + * @param[in] a The left operand of the addition. + * @param[in] b The right operand of the addition. + * @return The result of the saturating addition operation. + */ template requires std::integral T saturating_add(T a, T b) { @@ -478,13 +578,25 @@ namespace yycc::num::safe_op { if constexpr (std::is_unsigned_v) { return Limits::max(); } else { - // 溢出只可能发生在同号情况,因此只判定其中一个操作数的符号即可。 + // Overflow only occurs when 2 operand have same sign. + // So we simply check the sign of one of them. return (a > 0) ? Limits::max() : Limits::min(); } } return result; } + /** + * @brief Performs a saturating subtraction operation on two integers. + * @details + * If an overflow occurs during the subtraction, + * the result will be saturated to the maximum/minimum value, + * according to the the direction of overflow (overflow or underflow). + * @tparam T Integer type. + * @param[in] a The left operand of the subtraction. + * @param[in] b The right operand of the subtraction. + * @return The result of the saturating subtraction operation. + */ template requires std::integral T saturating_sub(T a, T b) { @@ -494,14 +606,26 @@ namespace yycc::num::safe_op { if constexpr (std::is_unsigned_v) { return 0; } else { - // 溢出只可能发生在异号情况,因此只判定两个操作数的大小即可。 - // a < b,则a为负,否则a为正 + // Overflow only occurs when 2 operand have different sign. + // So we simply compare these 2 operand. + // If a < b, then "a" is negative, otherwise positive. return (a < b) ? Limits::min() : Limits::max(); } } return result; } + /** + * @brief Performs a saturating multiplication operation on two integers. + * @details + * If an overflow occurs during the multiplication, + * the result will be saturated to the maximum/minimum value, + * according to the the direction of overflow (overflow or underflow). + * @tparam T Integer type. + * @param[in] a The left operand of the multiplication. + * @param[in] b The right operand of the multiplication. + * @return The result of the saturating multiplication operation. + */ template requires std::integral T saturating_mul(T a, T b) { @@ -511,19 +635,32 @@ namespace yycc::num::safe_op { if constexpr (std::is_unsigned_v) { return Limits::max(); } else { - // 做异号判定,如果XOR为true,则为异号 + // Check whether 2 operands have different sign. + // If the result of XOR is true, these 2 operands should have different sign. return ((a ^ b) < 0) ? Limits::min() : Limits::max(); } } return result; } + /** + * @brief Performs a saturating division operation on two integers. + * @details + * If an overflow occurs during the division, + * the result will be saturated to the maximum/minimum value, + * according to the the direction of overflow (overflow or underflow). + * @tparam T Integer type. + * @param[in] a The left operand of the division. + * @param[in] b The right operand of the division. + * @return The result of the saturating division operation. + * @exception std::logic_error If division by zero occurs. + */ template requires std::integral T saturating_div(T a, T b) { - // 除以0需要panic + // Division by zero is undefined behavior. if (ub_div_zero(a, b)) throw std::logic_error("div with zero"); - // 如果发生最小值除以0的情况,那么溢出到最大值 + // `INT_MIN / -1` overflow undefined behavior. if (ub_signed_int_min_div_minus_one(a, b)) { return std::numeric_limits::max(); } else { @@ -536,27 +673,57 @@ namespace yycc::num::safe_op { #pragma region Ordinary operations // YYC MARK: - // 以Rust的方式进行四则运算,不存在未定义行为。 - // 默认的四则运算与wrapping_*系列函数行为一致。 + // Rust-way add, sub, mul and div operators. + // There is no any undefined behavior which may occurs in these functions. + // These normal operator is just an alias to wrapping_* function family. + /** + * @brief Performs an addition operation on two integers. + * @tparam T Integer type. + * @param[in] a The left operand of the addition. + * @param[in] b The right operand of the addition. + * @return The result of the addition operation. + */ template requires std::integral T add(T a, T b) { return wrapping_add(a, b); } + /** + * @brief Performs a subtraction operation on two integers. + * @tparam T Integer type + * @param[in] a The left operand of the subtraction. + * @param[in] b The right operand of the subtraction. + * @return The result of the subtraction operation. + */ template requires std::integral T sub(T a, T b) { return wrapping_sub(a, b); } + /** + * @brief Performs a multiplication operation on two integers. + * @tparam T Integer type. + * @param[in] a The left operand of the multiplication. + * @param[in] b The right operand of the multiplication. + * @return The result of the multiplication operation. + */ template requires std::integral T mul(T a, T b) { return wrapping_mul(a, b); } + /** + * @brief Performs a division operation on two integers. + * @tparam T Integer type. + * @param[in] a The left operand of the division. + * @param[in] b The right operand of the division. + * @return The result of the division operation. + * @exception std::logic_error If division by zero or value overflow occurs. + */ template requires std::integral T div(T a, T b) { @@ -567,6 +734,7 @@ namespace yycc::num::safe_op { } // namespace yycc::num::safe_op -// YYC MARK: 删除宏定义,防止污染后面的内容 +// YYC MARK: +// Delete the macro definition to prevent polluting the content later. #undef YYCC_HARDWARE_OVERFLOW_GCC_FNS #undef YYCC_HARDWARE_OVERFLOW_WIN32_FNS diff --git a/src/yycc/string/op.cpp b/src/yycc/string/op.cpp index b95c44a..357149e 100644 --- a/src/yycc/string/op.cpp +++ b/src/yycc/string/op.cpp @@ -1,5 +1,4 @@ #include "op.hpp" - #include namespace yycc::string::op { diff --git a/testbench/yycc/num/op.cpp b/testbench/yycc/num/op.cpp index 8e71056..100a567 100644 --- a/testbench/yycc/num/op.cpp +++ b/testbench/yycc/num/op.cpp @@ -2,12 +2,18 @@ #include #include +#include + #define OP ::yycc::num::op namespace yycctest::num::op { TEST(NumOp, DivCeil) { - EXPECT_EQ(OP::div_ceil(7, 4), UINT32_C(2)); + // Normal case + EXPECT_EQ(OP::div_ceil(8, 4), UINT32_C(2)); + EXPECT_EQ(OP::div_ceil(7, 4), UINT32_C(2)); + // Limit case + EXPECT_EQ(OP::div_ceil(255, 2), UINT8_C(128)); } }