From ab4bd8eac5864d7d926b766cebf9a3d7e20cde29 Mon Sep 17 00:00:00 2001 From: Shreyas Kalyan Date: Thu, 18 Jul 2024 15:17:55 -0400 Subject: [PATCH 01/11] MONGOCRYPT-702 FLE2 range floating point correctness issue when using precision mode --- src/mc-range-encoding-private.h | 5 +- src/mc-range-encoding.c | 281 +++++++++++++++++++++++++------- src/mlib/int128.h | 17 ++ test/test-mc-range-encoding.c | 222 ++++++++++++++++++++----- test/test-mc-range-mincover.c | 15 +- 5 files changed, 439 insertions(+), 101 deletions(-) diff --git a/src/mc-range-encoding-private.h b/src/mc-range-encoding-private.h index 828173020..a5766760f 100644 --- a/src/mc-range-encoding-private.h +++ b/src/mc-range-encoding-private.h @@ -20,6 +20,7 @@ #include "mc-dec128.h" #include "mc-optional-private.h" #include "mongocrypt-status-private.h" +#include "mongocrypt.h" #include @@ -87,7 +88,7 @@ typedef struct { } mc_getTypeInfoDouble_args_t; // `mc_canUsePrecisionModeDouble` returns true if the domain can be represented in fewer than 64 bits. -bool mc_canUsePrecisionModeDouble(double min, double max, uint32_t precision, uint32_t *maxBitsOut); +bool mc_canUsePrecisionModeDouble(double min, double max, uint32_t precision, uint32_t *maxBitsOut, mongocrypt_status_t *status); /* mc_getTypeInfoDouble encodes the double `args.value` into an OSTType_Double * `out`. Returns false and sets `status` on error. */ @@ -111,7 +112,7 @@ typedef struct { } mc_getTypeInfoDecimal128_args_t; // `mc_canUsePrecisionModeDecimal` returns true if the domain can be represented in fewer than 128 bits. -bool mc_canUsePrecisionModeDecimal(mc_dec128 min, mc_dec128 max, uint32_t precision, uint32_t *maxBitsOut); +bool mc_canUsePrecisionModeDecimal(mc_dec128 min, mc_dec128 max, uint32_t precision, uint32_t *maxBitsOut, mongocrypt_status_t *status); /** * @brief Obtain the OST encoding of a finite Decimal128 value. diff --git a/src/mc-range-encoding.c b/src/mc-range-encoding.c index fa5ae3384..b583a6454 100644 --- a/src/mc-range-encoding.c +++ b/src/mc-range-encoding.c @@ -162,33 +162,98 @@ bool mc_getTypeInfo64(mc_getTypeInfo64_args_t args, mc_OSTType_Int64 *out, mongo } #define exp10Double(x) pow(10, x) +#define SCALED_DOUBLE_BOUNDS 9223372036854775807.0 // 2^63 - 1 +#define UINT_64_MAX 18446744073709551615ull -bool mc_canUsePrecisionModeDouble(double min, double max, uint32_t precision, uint32_t *maxBitsOut) { +uint64_t subtract_int64_t(int64_t max, int64_t min) { + if (max > 0 && min > 0 || max < 0 && min < 0) { + return (uint64_t) (max - min); + } + + uint64_t u_return = (uint64_t) labs(max); + u_return += (uint64_t) labs(min); + return u_return; +} + +bool ceil_log2_double(uint64_t i, uint32_t *maxBitsOut, mongocrypt_status_t *status) { + if (i == 0) { + CLIENT_ERR("Invalid input to ceil_log2_double function. Input cannot be 0."); + return false; + } + + uint32_t clz = (uint32_t) _mlibCountLeadingZeros_u64(i); + uint32_t bits; + if ((i & (i - 1)) == 0) { + bits = 64 - clz - 1; + } else { + bits = 64 - clz; + } + *maxBitsOut = bits; + return true; +} + +bool mc_canUsePrecisionModeDouble(double min, double max, uint32_t precision, uint32_t *maxBitsOut, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(maxBitsOut); - bool use_precision_mode = false; - double range = max - min; - - // We can overflow if max = max double and min = min double so make sure - // we have finite number after we do subtraction - // Ignore conversion warnings to fix error with glibc. - if (mc_isfinite(range)) { - // This creates a range which is wider then we permit by our min/max - // bounds check with the +1 but it is as the algorithm is written in - // WRITING-11907. - double rangeAndPrecision = (range + 1) * exp10Double(precision); - - if (mc_isfinite(rangeAndPrecision)) { - double bits_range_double = log2(rangeAndPrecision); - *maxBitsOut = (uint32_t)ceil(bits_range_double); - - if (*maxBitsOut < 64) { - use_precision_mode = true; - } - } + if (min > max) { + CLIENT_ERR("Invalid bounds for double range precision, max must be less than min. min: %g, max: %g", min, max); + return false; + } + + const double scaled_prc = exp10Double(precision); + + const double scaled_max = max * scaled_prc; + const double scaled_min = min * scaled_prc; + + if (scaled_max != trunc(scaled_max)) { + CLIENT_ERR("Invalid upper bounds for double precision. Digits after the decimal must be less than specified precision value. max: %g", max); + return false; + } + + if (scaled_min != trunc(scaled_min)) { + CLIENT_ERR("Invalid lower bounds for double precision. Digits after the decimal must be less than specified precision value. min: %g", min); + return false; + } + + if (fabs(scaled_max) >= SCALED_DOUBLE_BOUNDS) { + CLIENT_ERR("Invalid upper bounds for double precision. abs(max) must be less than 9223372036854775807. max: %g", max); + return false; + } + + if (fabs(scaled_min) >= SCALED_DOUBLE_BOUNDS) { + CLIENT_ERR("Invalid lower bounds for double precision. abs(min) must be less than 9223372036854775807. min: %g", min); + return false; + } + + const double t_1 = scaled_max - scaled_min; + const double t_4 = UINT_64_MAX - t_1; + const double t_5 = floor(log10(t_4)) - 1; + + if ((double) precision >= t_5) { + CLIENT_ERR("Invalid value for precision. precision: %" PRId32, precision); + return false; } - return use_precision_mode; + const int64_t i_1 = (int64_t) (scaled_max); + const int64_t i_2 = (int64_t) (scaled_min); + + const uint64_t range = subtract_int64_t(i_1, i_2); + const uint64_t i_3 = range + (uint64_t) (scaled_prc); + + if (i_3 <= 0) { + CLIENT_ERR("Invalid value for upper and lower bounds for double precision. Min must be less than max. min: %g, max: %g", min, max); + return false; + } + + if (!ceil_log2_double(i_3, maxBitsOut, status)) { + return false; + } + + if (*maxBitsOut >= 64) { + return false; + } + + return true; } bool mc_getTypeInfoDouble(mc_getTypeInfoDouble_args_t args, @@ -248,8 +313,13 @@ bool mc_getTypeInfoDouble(mc_getTypeInfoDouble_args_t args, } use_precision_mode = - mc_canUsePrecisionModeDouble(args.min.value, args.max.value, args.precision.value, &bits_range); + mc_canUsePrecisionModeDouble(args.min.value, args.max.value, args.precision.value, &bits_range, status); + if (!use_precision_mode && use_range_v2) { + if (!mongocrypt_status_ok(status)) { + return false; + } + CLIENT_ERR("The domain of double values specified by the min, max, and precision cannot be represented in " "fewer than 64 bits. min: %g, max: %g, precision: %" PRIu32, args.min.value, @@ -257,6 +327,11 @@ bool mc_getTypeInfoDouble(mc_getTypeInfoDouble_args_t args, args.precision.value); return false; } + + // If we are not in range_v2, then we don't care about the error returned + // from canUsePrecisionMode so we can reset the status. + _mongocrypt_status_reset(status); + } if (use_precision_mode) { @@ -319,7 +394,7 @@ bool mc_getTypeInfoDouble(mc_getTypeInfoDouble_args_t args, * @param dec * @return mlib_int128 */ -static mlib_int128 dec128_to_int128(mc_dec128 dec) { +static mlib_int128 dec128_to_uint128(mc_dec128 dec) { // Only normal numbers BSON_ASSERT(mc_dec128_is_finite(dec)); BSON_ASSERT(!mc_dec128_is_nan(dec)); @@ -345,42 +420,124 @@ static mlib_int128 dec128_to_int128(mc_dec128 dec) { return ret; } -bool mc_canUsePrecisionModeDecimal(mc_dec128 min, mc_dec128 max, uint32_t precision, uint32_t *maxBitsOut) { +#define SIGNED_INT_128_MAX_DECIMAL mc_dec128_from_string("170141183460469231731687303715884105727") +#define UNSIGNED_INT_128_MAX_DECIMAL mc_dec128_from_string("340282366920938463463374607431768211455") + +static mlib_int128 dec128_to_int128(mc_dec128 dec) { + BSON_ASSERT(mc_dec128_less(dec, SIGNED_INT_128_MAX_DECIMAL)); + + bool negative = false; + + if (mc_dec128_is_negative(dec)) { + negative = true; + dec = mc_dec128_mul(MC_DEC128(-1), dec); + } + + mlib_int128 ret_val = dec128_to_uint128(dec); + + if (negative) { + ret_val = mlib_int128_mul(MLIB_INT128(-1), ret_val); + } + + return ret_val; +} + +bool ceil_log2_int128(mlib_int128 i, uint32_t *maxBitsOut, mongocrypt_status_t *status) { + if (mlib_int128_eq(i, MLIB_INT128(0))) { + CLIENT_ERR("Invalid input to ceil_log2_int128 function. Input cannot be 0."); + return false; + } + + uint32_t clz = (uint32_t) _mlibCountLeadingZeros_u128(i); + uint32_t bits; + + // if i & (i - 1) == 0 + if (mlib_int128_eq((mlib_int128_bitand(i, (mlib_int128_sub(i, MLIB_INT128(1))))), MLIB_INT128(0))) { + bits = 128 - clz - 1; + } else { + bits = 128 - clz; + } + *maxBitsOut = bits; + return true; +} + +bool mc_canUsePrecisionModeDecimal(mc_dec128 min, mc_dec128 max, uint32_t precision, uint32_t *maxBitsOut, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(maxBitsOut); - bool use_precision_mode = false; - // max - min - mc_dec128 bounds_n1 = mc_dec128_sub(max, min); - // The size of [min, max]: (max - min) + 1 - mc_dec128 bounds = mc_dec128_add(bounds_n1, MC_DEC128_ONE); - - // We can overflow if max = max_dec128 and min = min_dec128 so make sure - // we have finite number after we do subtraction - if (mc_dec128_is_finite(bounds)) { - // This creates a range which is wider then we permit by our min/max - // bounds check with the +1 but it is as the algorithm is written in - // WRITING-11907. - mc_dec128 precision_scaled_bounds = mc_dec128_scale(bounds, precision); - /// The number of bits required to hold the result for the given - /// precision (as decimal) - mc_dec128 bits_range_dec = mc_dec128_log2(precision_scaled_bounds); - - if (mc_dec128_is_finite(bits_range_dec) && mc_dec128_less(bits_range_dec, MC_DEC128(128))) { - // We need fewer than 128 bits to hold the result. But round up, - // just to be sure: - int64_t r = mc_dec128_to_int64(mc_dec128_round_integral_ex(bits_range_dec, MC_DEC128_ROUND_UPWARD, NULL)); - BSON_ASSERT(r >= 0); - BSON_ASSERT(r <= UINT8_MAX); - // We've computed the proper 'bits_range' - *maxBitsOut = (uint8_t)r; - - if (*maxBitsOut < 128) { - use_precision_mode = true; - } - } + if (!mc_dec128_is_finite(max)) { + CLIENT_ERR("Invalid upper bounds for Decimal128 precision. Max is infinite."); + return false; + } + + if (!mc_dec128_is_finite(min)) { + CLIENT_ERR("Invalid lower bounds for Decimal128 precision. Min is infinite."); + return false; + } + + if (mc_dec128_greater_equal(min, max)) { + CLIENT_ERR("Invalid upper and lower bounds for Decimal128 precision. Min must be strictly less than max. min: %s, max: %s", mc_dec128_to_string(min).str, mc_dec128_to_string(max).str); + return false; + } + + mc_dec128 scaled_max = mc_dec128_scale(max, precision); + mc_dec128 scaled_min = mc_dec128_scale(min, precision); + + mc_dec128 scaled_max_trunc = mc_dec128_round_integral_ex(scaled_max, MC_DEC128_ROUND_TOWARD_ZERO, NULL); + mc_dec128 scaled_min_trunc = mc_dec128_round_integral_ex(scaled_min, MC_DEC128_ROUND_TOWARD_ZERO, NULL); + + if (mc_dec128_not_equal(scaled_max, scaled_max_trunc)) { + CLIENT_ERR("Invalid upper bounds for Decimal128 precision. Digits after the decimal must be less than specified precision value. max: %s, precision: %" PRIu32, mc_dec128_to_string(max).str, precision); + return false; + } + + if (mc_dec128_not_equal(scaled_min, scaled_min_trunc)) { + CLIENT_ERR("Invalid lower bounds for Decimal128 precision. Digits after the decimal must be less than specified precision value. min: %s, precision: %" PRIu32, mc_dec128_to_string(min).str, precision); + return false; + } + + if (mc_dec128_greater(mc_dec128_abs(scaled_max), SIGNED_INT_128_MAX_DECIMAL)) { + CLIENT_ERR("Invalid upper bounds for Decimal128 precision, absolute scaled value must be less than 170141183460469231731687303715884105727. max: %s", mc_dec128_to_string(max).str); + return false; + } + + if (mc_dec128_greater(mc_dec128_abs(scaled_min), SIGNED_INT_128_MAX_DECIMAL)) { + CLIENT_ERR("Invalid lower bounds for Decimal128 precision, absolute scaled value must be less than 170141183460469231731687303715884105727. min: %s", mc_dec128_to_string(min).str); + return false; } - return use_precision_mode; + mc_dec128 t_1 = mc_dec128_sub(scaled_max, scaled_min); + mc_dec128 t_4 = mc_dec128_sub(UNSIGNED_INT_128_MAX_DECIMAL, t_1); + mc_dec128 t_5 = mc_dec128_sub(mc_dec128_round_integral_ex(mc_dec128_log10(t_4), MC_DEC128_ROUND_UPWARD, NULL), MC_DEC128(1)); + + if (mc_dec128_less(t_5, MC_DEC128(precision))) { + CLIENT_ERR("Invalid value for precision. precision: %" PRIu32, precision); + return false; + } + + + mlib_int128 i_1 = dec128_to_int128(scaled_max); + mlib_int128 i_2 = dec128_to_int128(scaled_min); + + // Because we have guaranteed earlier that max is greater than min, we can + // subtract these values and guarantee that taking their unsigned + // representation will yield the actual range result. + mlib_int128 range128 = mlib_int128_sub(i_1, i_2); + + if (precision > 255) { + CLIENT_ERR("Invalid value for precision. Must be less than 255. precision: %" PRIu32, precision); + return false; + } + + mlib_int128 i_3 = mlib_int128_add(range128, mlib_int128_pow10((uint8_t) precision)); + if (!ceil_log2_int128(i_3, maxBitsOut, status)) { + return false; + } + + if (*maxBitsOut >= 128) { + return false; + } + + return true; } bool mc_getTypeInfoDecimal128(mc_getTypeInfoDecimal128_args_t args, @@ -445,9 +602,13 @@ bool mc_getTypeInfoDecimal128(mc_getTypeInfoDecimal128_args_t args, } use_precision_mode = - mc_canUsePrecisionModeDecimal(args.min.value, args.max.value, args.precision.value, &bits_range); + mc_canUsePrecisionModeDecimal(args.min.value, args.max.value, args.precision.value, &bits_range, status); + + if (use_range_v2 && !use_precision_mode) { + if (!mongocrypt_status_ok(status)) { + return false; + } - if (!use_precision_mode && use_range_v2) { CLIENT_ERR("The domain of decimal values specified by the min, max, and precision cannot be represented in " "fewer than 128 bits. min: %s, max: %s, precision: %" PRIu32, mc_dec128_to_string(args.min.value).str, @@ -455,6 +616,10 @@ bool mc_getTypeInfoDecimal128(mc_getTypeInfoDecimal128_args_t args, args.precision.value); return false; } + + // If we are not in range_v2, then we don't care about the error returned + // from canUsePrecisionMode so we can reset the status. + _mongocrypt_status_reset(status); } // Constant zero diff --git a/src/mlib/int128.h b/src/mlib/int128.h index 1619d200f..d43338392 100644 --- a/src/mlib/int128.h +++ b/src/mlib/int128.h @@ -200,6 +200,13 @@ static mlib_constexpr_fn mlib_int128 mlib_int128_bitor(mlib_int128 l, mlib_int12 return MLIB_INIT(mlib_int128) MLIB_INT128_FROM_PARTS(l.r.lo | r.r.lo, l.r.hi | r.r.hi); } +/** + * @brief Bitwise-and two 128-bit integers + */ +static mlib_constexpr_fn mlib_int128 mlib_int128_bitand(mlib_int128 l, mlib_int128 r) { + return MLIB_INIT(mlib_int128) MLIB_INT128_FROM_PARTS(l.r.lo & r.r.lo, l.r.hi & r.r.hi); +} + // Multiply two 64bit integers to get a 128-bit result without overflow static mlib_constexpr_fn mlib_int128 _mlibUnsignedMult128(uint64_t left, uint64_t right) { // Perform a Knuth 4.3.1M multiplication @@ -245,6 +252,16 @@ static mlib_constexpr_fn int _mlibCountLeadingZeros_u64(uint64_t bits) { return n; } +static mlib_constexpr_fn int _mlibCountLeadingZeros_u128(mlib_int128 r) { + int clz_l = _mlibCountLeadingZeros_u64(r.r.hi); + if (clz_l != 64) { + return clz_l; + } + + int clz_r = _mlibCountLeadingZeros_u64(r.r.lo); + return clz_l + clz_r; +} + /// Implementation of Knuth's algorithm 4.3.1 D for unsigned integer division static mlib_constexpr_fn void _mlibKnuth431D(uint32_t *const u, const int ulen, const uint32_t *const v, const int vlen, uint32_t *quotient) { diff --git a/test/test-mc-range-encoding.c b/test/test-mc-range-encoding.c index 905047ae1..8cec2a3f3 100644 --- a/test/test-mc-range-encoding.c +++ b/test/test-mc-range-encoding.c @@ -193,6 +193,63 @@ static void _test_RangeTest_Encode_Int64(_mongocrypt_tester_t *tester) { } } +#define INT_64_MAX_DOUBLE (double) 18446744073709551615ull + +static void _test_canUsePrecisionModeDouble(_mongocrypt_tester_t *tester) { +#define CAN_USE_PRECISION_MODE(lb, ub, prc, expected, expected_bits_out) \ + { \ + uint32_t bits_out = 0; \ + mongocrypt_status_t *const status = mongocrypt_status_new(); \ + printf("_test_canUsePrecisionModeDecimal, min: %f, max: %f, prc: %" PRIu32, \ + lb, \ + ub, \ + prc); \ + bool result = mc_canUsePrecisionModeDouble(lb, ub, prc, &bits_out, status); \ + ASSERT_OK_STATUS(mongocrypt_status_ok(status), status); \ + ASSERT(result == expected); \ + ASSERT_CMPINT32(expected_bits_out, ==, bits_out); \ + mongocrypt_status_destroy(status); \ + } + +#define CAN_USE_PRECISION_MODE_ERRORS(lb, ub, prc, error) \ + { \ + mongocrypt_status_t *const status = mongocrypt_status_new(); \ + printf("_test_canUsePrecisionModeDecimal errors, min: %f, max: %f, prc: %" PRIu32, \ + lb, \ + ub, \ + prc); \ + uint32_t bits_out = 0; \ + bool result = mc_canUsePrecisionModeDouble(lb, ub, prc, &bits_out, status); \ + ASSERT_OR_PRINT_MSG(!result, "expected error, but got none"); \ + ASSERT_STATUS_CONTAINS(status, error); \ + mongocrypt_status_destroy(status); \ + } + + CAN_USE_PRECISION_MODE(1.0, 16.0, 0, true, 4); + CAN_USE_PRECISION_MODE(0.0, 16.0, 0, true, 5); + // 2^53 + 1 is where double starts to lose precision, so we need to ensure that we get the + // correct value for max_bits out. + CAN_USE_PRECISION_MODE(1.0, 9007199254740992.0, 0, true, 53); + CAN_USE_PRECISION_MODE(0.0, 9007199254740992.0, 0, true, 54); + + CAN_USE_PRECISION_MODE(2.718281, 314.159265, 6, true, 29); + + CAN_USE_PRECISION_MODE(-1000000000.0, 9223372036844775424.0, 0, false, 64); + + CAN_USE_PRECISION_MODE_ERRORS(2.710000, 314.150000, 2, "Invalid upper bounds for double precision. Digits after"); + CAN_USE_PRECISION_MODE_ERRORS(314.150000, 350.0, 2, "Invalid lower bounds for double precision. Digits after"); + + CAN_USE_PRECISION_MODE_ERRORS( + (double) 9007199254740992, INT_64_MAX_DOUBLE, 0, "Invalid upper bounds for double precision. abs(max) must"); + CAN_USE_PRECISION_MODE_ERRORS( + -1 * INT_64_MAX_DOUBLE, -1 * (double) 9007199254740992, 0, "Invalid lower bounds for double precision. abs(min) must"); + CAN_USE_PRECISION_MODE_ERRORS(-92233720368547.0, 92233720368547.0, 5, "Invalid value for precision."); + +#undef CAN_USE_PRECISION_MODE +#undef CAN_USE_PRECISION_MODE_ERRORS +} + + typedef struct { double value; mc_optional_double_t min; @@ -319,18 +376,15 @@ static void _test_RangeTest_Encode_Double(_mongocrypt_tester_t *tester) { .min = OPT_DOUBLE_C(-DBL_MAX), .precision = OPT_U32_C(3), // Applying min/max/precision result in a domain needing >= 64 bits to represent. - // For range v1, expect precision to be ignored. - .use_range_v1 = true, - .expect = UINT64_C(9223372036854775808), - .expectMax = OPT_U64_C(UINT64_MAX)}, + // For range v2, expect an error. + .expectError = "Invalid upper bounds for double precision."}, {.value = 0, .max = OPT_DOUBLE_C(DBL_MAX), .min = OPT_DOUBLE_C(-DBL_MAX), .precision = OPT_U32_C(3), // Applying min/max/precision result in a domain needing >= 64 bits to represent. // For range v2, expect an error. - .expectError = "The domain of double values specified by the min, max, and precision cannot " - "be represented in fewer than 64 bits"}, + .expectError = "Invalid upper bounds for double precision."}, {.value = 3.141592653589, .max = OPT_DOUBLE_C(5), .min = OPT_DOUBLE_C(0), @@ -372,18 +426,15 @@ static void _test_RangeTest_Encode_Double(_mongocrypt_tester_t *tester) { .min = OPT_DOUBLE_C(-DBL_MAX), .precision = OPT_U32_C(3), // Applying min/max/precision result in a domain needing >= 64 bits to represent. - // For range v1, expect precision to be ignored. - .use_range_v1 = true, - .expect = 15326393489903895421ULL, - .expectMax = OPT_U64_C(UINT64_MAX)}, + // For range v2, expect an error. + .expectError = "Invalid upper bounds for double precision."}, {.value = 1E100, .max = OPT_DOUBLE_C(DBL_MAX), .min = OPT_DOUBLE_C(-DBL_MAX), .precision = OPT_U32_C(3), // Applying min/max/precision result in a domain needing >= 64 bits to represent. // For range v2, expect an error. - .expectError = "The domain of double values specified by the min, max, and precision cannot " - "be represented in fewer than 64 bits"}, + .expectError = "Invalid upper bounds for double precision."}, {.value = 1E9, .max = OPT_DOUBLE_C(1E10), .min = OPT_DOUBLE_C(0), @@ -413,18 +464,15 @@ static void _test_RangeTest_Encode_Double(_mongocrypt_tester_t *tester) { .min = OPT_DOUBLE_C(1E-30), .precision = OPT_U32_C(35), // Applying min/max/precision result in a domain needing >= 64 bits to represent. - // For range v1, expect precision to be ignored. - .use_range_v1 = true, - .expect = 13381399884061196960ULL, - .expectMax = OPT_U64_C(UINT64_MAX)}, + // For range v2, expect an error. + .expectError = "Invalid upper bounds for double precision."}, {.value = 1E-30, .max = OPT_DOUBLE_C(10E-30), .min = OPT_DOUBLE_C(1E-30), .precision = OPT_U32_C(35), // Applying min/max/precision result in a domain needing >= 64 bits to represent. // For range v2, expect an error. - .expectError = "The domain of double values specified by the min, max, and precision cannot " - "be represented in fewer than 64 bits"}, + .expectError = "Invalid upper bounds for double precision."}, /* Test cases copied from Double_Bounds_Precision ... end */ {.value = -1, .min = OPT_DOUBLE_C(0), @@ -485,6 +533,80 @@ typedef struct { bool use_range_v1; // By default, use range v2. } Decimal128Test; +static void _test_canUsePrecisionModeDecimal(_mongocrypt_tester_t *tester) { +#define CAN_USE_PRECISION_MODE(lb, ub, prc, expected, expected_bits_out) \ + { \ + uint32_t bits_out = 0; \ + mongocrypt_status_t *const status = mongocrypt_status_new(); \ + printf("_test_canUsePrecisionModeDecimal, min: %s, max: %s, prc: %" PRIu32, \ + mc_dec128_to_string(lb).str, \ + mc_dec128_to_string(ub).str, \ + prc); \ + bool result = mc_canUsePrecisionModeDecimal(lb, ub, prc, &bits_out, status); \ + ASSERT_OK_STATUS(mongocrypt_status_ok(status), status); \ + ASSERT(result == expected); \ + ASSERT_CMPINT32(expected_bits_out, ==, bits_out); \ + mongocrypt_status_destroy(status); \ + } + +#define CAN_USE_PRECISION_MODE_ERRORS(lb, ub, prc, error) \ + { \ + mongocrypt_status_t *const status = mongocrypt_status_new(); \ + printf("_test_canUsePrecisionModeDecimal errors, min: %s, max: %s, prc: %" PRIu32, \ + mc_dec128_to_string(lb).str, \ + mc_dec128_to_string(ub).str, \ + prc); \ + uint32_t bits_out = 0; \ + bool result = mc_canUsePrecisionModeDecimal(lb, ub, prc, &bits_out, status); \ + ASSERT_OR_PRINT_MSG(!result, "expected error, but got none"); \ + ASSERT_STATUS_CONTAINS(status, error); \ + mongocrypt_status_destroy(status); \ + } + + CAN_USE_PRECISION_MODE(MC_DEC128(1), MC_DEC128(16), 0, true, 4); + CAN_USE_PRECISION_MODE(MC_DEC128(0), MC_DEC128(16), 0, true, 5); + + // It is unclear where Decimal128 looses precision, so we choose an arbitrarily large value + // and make sure that max_bits is correct for that boundary. + CAN_USE_PRECISION_MODE( + MC_DEC128(1), mc_dec128_from_string("324518553658426726783156020576256"), 0, true, 108); + CAN_USE_PRECISION_MODE( + MC_DEC128(0), mc_dec128_from_string("324518553658426726783156020576256"), 0, true, 109); + + CAN_USE_PRECISION_MODE(mc_dec128_from_string("-100000000000000000000000000000000"), + mc_dec128_from_string("170141183460469231731687303715880000000"), + 0, + false, + 128); + + CAN_USE_PRECISION_MODE_ERRORS( + mc_dec128_from_string("788545.12392843"), mc_dec128_from_string("4607431769000000.129834923"), 4, "Invalid upper bounds for Decimal128 precision. Digits after"); + CAN_USE_PRECISION_MODE_ERRORS( + mc_dec128_from_string("788545.12392843"), mc_dec128_from_string("7885451.2"), 4, "Invalid lower bounds for Decimal128 precision. Digits after"); + CAN_USE_PRECISION_MODE_ERRORS(mc_dec128_from_string("324518553658426726783156020576256"), + mc_dec128_from_string("340282366920938463463374607431768211455"), + 10, + "Invalid upper bounds for Decimal128 precision, absolute scaled value must be"); + + CAN_USE_PRECISION_MODE_ERRORS(mc_dec128_from_string("-340282366920938463463374607431768211455"), + mc_dec128_from_string("-3245185536584267267831560"), + 10, + "Invalid lower bounds for Decimal128 precision, absolute scaled value must be"); + + CAN_USE_PRECISION_MODE_ERRORS(mc_dec128_from_string("-17014118346046923173168730371588.0000000"), + mc_dec128_from_string("17014118346046923173168730371588.0000000"), + 7, + "Invalid value for precision"); + + CAN_USE_PRECISION_MODE_ERRORS(MC_DEC128(788545.000000), + mc_dec128_from_string("340282366920938463463374607431769000000.000000"), + 0, + "Invalid upper bounds for Decimal128 precision, absolute scaled value must be"); + +#undef CAN_USE_PRECISION_MODE +#undef CAN_USE_PRECISION_MODE_ERRORS +} + static void _test_RangeTest_Encode_Decimal128(_mongocrypt_tester_t *tester) { Decimal128Test tests[] = { #define CASE(Value, ExpectStr) \ @@ -717,21 +839,39 @@ static void _test_RangeTest_Encode_Decimal128(_mongocrypt_tester_t *tester) { "cannot be represented in fewer than 128 bits" \ } - ASSERT_EIBB(0, 1, -1, 3, 1000), - ASSERT_EIBB(0, 1, -1E5, 3, 100000000), +#define ASSERT_EIBB_ERROR(Val, Max, Min, Precision, Expect, Error) \ + (Decimal128Test){ \ + .value = mc_dec128_from_string(#Val), \ + .min = OPT_MC_DEC128(mc_dec128_from_string(#Min)), \ + .max = OPT_MC_DEC128(mc_dec128_from_string(#Max)), \ + .precision = OPT_U32(Precision), \ + .expect = Expect, \ + .use_range_v1 = true, \ + }, \ + (Decimal128Test) { \ + .value = mc_dec128_from_string(#Val), .min = OPT_MC_DEC128(mc_dec128_from_string(#Min)), \ + .max = OPT_MC_DEC128(mc_dec128_from_string(#Max)), .precision = OPT_U32(Precision), \ + .expectError = Error \ + } + + // ASSERT_EIBB(0, 1, -1, 3, 1000), + // ASSERT_EIBB(0, 1, -1E5, 3, 100000000), - ASSERT_EIBB(-1E-33, 1, -1E5, 3, 100000000), + // ASSERT_EIBB(-1E-33, 1, -1E5, 3, 100000000), - ASSERT_EIBB_OVERFLOW(0, - MC_DEC128_LARGEST_POSITIVE, - MC_DEC128_LARGEST_NEGATIVE, - 3, - mlib_int128_from_string("170141183460469231731687303715884105728", NULL)), - ASSERT_EIBB_OVERFLOW(0, - DBL_MAX, - DBL_MIN, - 3, - mlib_int128_from_string("170141183460469231731687303715884105728", NULL)), + ASSERT_EIBB_ERROR(0, + MC_DEC128_LARGEST_POSITIVE, + MC_DEC128_LARGEST_NEGATIVE, + 3, + mlib_int128_from_string("170141183460469231731687303715884105728", NULL), + "Invalid upper bounds for Decimal128 precision. Max is infinite."), + + ASSERT_EIBB_ERROR(0, + DBL_MAX, + DBL_MIN, + 3, + mlib_int128_from_string("170141183460469231731687303715884105728", NULL), + "Invalid upper bounds for Decimal128 precision. Max is infinite."), ASSERT_EIBB(3.141592653589, 5, 0, 0, 3), ASSERT_EIBB(3.141592653589, 5, 0, 1, 31), @@ -743,11 +883,12 @@ static void _test_RangeTest_Encode_Decimal128(_mongocrypt_tester_t *tester) { ASSERT_EIBB(-5, -1, -10, 3, 5000), - ASSERT_EIBB_OVERFLOW(1E100, - DBL_MAX, - DBL_MIN, - 3, - mlib_int128_from_string("232572183460469231731687303715884099485", NULL)), + ASSERT_EIBB_ERROR(1E100, + DBL_MAX, + DBL_MIN, + 3, + mlib_int128_from_string("232572183460469231731687303715884099485", NULL), + "Invalid upper bounds for Decimal128 precision. Max is infinite."), ASSERT_EIBB(1E9, 1E10, 0, 3, 1000000000000), ASSERT_EIBB(1E9, 1E10, 0, 0, 1000000000), @@ -759,12 +900,13 @@ static void _test_RangeTest_Encode_Decimal128(_mongocrypt_tester_t *tester) { // Test a range that requires > 64 bits. ASSERT_EIBB(5, 18446744073709551616, .1, 1, 49), - // Test a range that requires > 64 bits. - // min has more places after the decimal than precision. - ASSERT_EIBB(5, 18446744073709551616, .01, 1, 49), + // // Test a range that requires > 64 bits. + // // min has more places after the decimal than precision. + // ASSERT_EIBB_ERROR(5, 18446744073709551616, .01, 1, 49, "Invalid lower bounds for Decimal128 precision. Digits after the decimal"), #undef ASSERT_EIBB #undef ASSERT_EIBB_OVERFLOW +#undef ASSERT_EIBB_ERROR /* Test cases copied from Decimal128_Bounds_Precision ... end */ }; @@ -814,8 +956,10 @@ static void _test_RangeTest_Encode_Decimal128(_mongocrypt_tester_t *tester) { void _mongocrypt_tester_install_range_encoding(_mongocrypt_tester_t *tester) { INSTALL_TEST(_test_RangeTest_Encode_Int32); INSTALL_TEST(_test_RangeTest_Encode_Int64); + INSTALL_TEST(_test_canUsePrecisionModeDouble); INSTALL_TEST(_test_RangeTest_Encode_Double); #if MONGOCRYPT_HAVE_DECIMAL128_SUPPORT + INSTALL_TEST(_test_canUsePrecisionModeDecimal); INSTALL_TEST(_test_RangeTest_Encode_Decimal128); #endif } diff --git a/test/test-mc-range-mincover.c b/test/test-mc-range-mincover.c index 2675cd03f..e05554514 100644 --- a/test/test-mc-range-mincover.c +++ b/test/test-mc-range-mincover.c @@ -177,10 +177,16 @@ static const char *_test_expectErrorDouble(void *tests, size_t idx) { if (test->min.set && test->max.set && test->precision.set) { // Expect an error for tests including an invalid min/max/precision. uint32_t ignored; - if (!mc_canUsePrecisionModeDouble(test->min.value, test->max.value, test->precision.value, &ignored)) { + mongocrypt_status_t *const status = mongocrypt_status_new(); + if (!mc_canUsePrecisionModeDouble(test->min.value, test->max.value, test->precision.value, &ignored, status)) { + if (!mongocrypt_status_ok(status)) { + return mongocrypt_status_message(status, NULL); + } + return "The domain of double values specified by the min, max, and precision cannot be represented in " "fewer than 64 bits"; } + mongocrypt_status_destroy(status); } return test->expectError; } @@ -192,7 +198,12 @@ static const char *_test_expectErrorDecimal128(void *tests, size_t idx) { if (test->min.set && test->max.set && test->precision.set) { // Expect an error for tests including an invalid min/max/precision. uint32_t ignored; - if (!mc_canUsePrecisionModeDecimal(test->min.value, test->max.value, test->precision.value, &ignored)) { + mongocrypt_status_t *const status = mongocrypt_status_new(); + if (!mc_canUsePrecisionModeDecimal(test->min.value, test->max.value, test->precision.value, &ignored, status)) { + if (!mongocrypt_status_ok(status)) { + return mongocrypt_status_message(status, NULL); + } + return "The domain of decimal values specified by the min, max, and precision cannot be represented in " "fewer than 128 bits"; } From cccc38e2710afd4e811187af8401df7341ccabb1 Mon Sep 17 00:00:00 2001 From: Shreyas Kalyan Date: Thu, 18 Jul 2024 15:35:36 -0400 Subject: [PATCH 02/11] lint --- src/mc-range-encoding-private.h | 12 ++- src/mc-range-encoding.c | 83 ++++++++++++------ test/test-mc-range-encoding.c | 147 ++++++++++++++++---------------- 3 files changed, 138 insertions(+), 104 deletions(-) diff --git a/src/mc-range-encoding-private.h b/src/mc-range-encoding-private.h index a5766760f..b1ab0d76b 100644 --- a/src/mc-range-encoding-private.h +++ b/src/mc-range-encoding-private.h @@ -88,7 +88,11 @@ typedef struct { } mc_getTypeInfoDouble_args_t; // `mc_canUsePrecisionModeDouble` returns true if the domain can be represented in fewer than 64 bits. -bool mc_canUsePrecisionModeDouble(double min, double max, uint32_t precision, uint32_t *maxBitsOut, mongocrypt_status_t *status); +bool mc_canUsePrecisionModeDouble(double min, + double max, + uint32_t precision, + uint32_t *maxBitsOut, + mongocrypt_status_t *status); /* mc_getTypeInfoDouble encodes the double `args.value` into an OSTType_Double * `out`. Returns false and sets `status` on error. */ @@ -112,7 +116,11 @@ typedef struct { } mc_getTypeInfoDecimal128_args_t; // `mc_canUsePrecisionModeDecimal` returns true if the domain can be represented in fewer than 128 bits. -bool mc_canUsePrecisionModeDecimal(mc_dec128 min, mc_dec128 max, uint32_t precision, uint32_t *maxBitsOut, mongocrypt_status_t *status); +bool mc_canUsePrecisionModeDecimal(mc_dec128 min, + mc_dec128 max, + uint32_t precision, + uint32_t *maxBitsOut, + mongocrypt_status_t *status); /** * @brief Obtain the OST encoding of a finite Decimal128 value. diff --git a/src/mc-range-encoding.c b/src/mc-range-encoding.c index b583a6454..a1343ecdd 100644 --- a/src/mc-range-encoding.c +++ b/src/mc-range-encoding.c @@ -162,16 +162,16 @@ bool mc_getTypeInfo64(mc_getTypeInfo64_args_t args, mc_OSTType_Int64 *out, mongo } #define exp10Double(x) pow(10, x) -#define SCALED_DOUBLE_BOUNDS 9223372036854775807.0 // 2^63 - 1 +#define SCALED_DOUBLE_BOUNDS 9223372036854775807.0 // 2^63 - 1 #define UINT_64_MAX 18446744073709551615ull uint64_t subtract_int64_t(int64_t max, int64_t min) { if (max > 0 && min > 0 || max < 0 && min < 0) { - return (uint64_t) (max - min); + return (uint64_t)(max - min); } - uint64_t u_return = (uint64_t) labs(max); - u_return += (uint64_t) labs(min); + uint64_t u_return = (uint64_t)labs(max); + u_return += (uint64_t)labs(min); return u_return; } @@ -181,7 +181,7 @@ bool ceil_log2_double(uint64_t i, uint32_t *maxBitsOut, mongocrypt_status_t *sta return false; } - uint32_t clz = (uint32_t) _mlibCountLeadingZeros_u64(i); + uint32_t clz = (uint32_t)_mlibCountLeadingZeros_u64(i); uint32_t bits; if ((i & (i - 1)) == 0) { bits = 64 - clz - 1; @@ -192,7 +192,11 @@ bool ceil_log2_double(uint64_t i, uint32_t *maxBitsOut, mongocrypt_status_t *sta return true; } -bool mc_canUsePrecisionModeDouble(double min, double max, uint32_t precision, uint32_t *maxBitsOut, mongocrypt_status_t *status) { +bool mc_canUsePrecisionModeDouble(double min, + double max, + uint32_t precision, + uint32_t *maxBitsOut, + mongocrypt_status_t *status) { BSON_ASSERT_PARAM(maxBitsOut); if (min > max) { @@ -206,22 +210,28 @@ bool mc_canUsePrecisionModeDouble(double min, double max, uint32_t precision, ui const double scaled_min = min * scaled_prc; if (scaled_max != trunc(scaled_max)) { - CLIENT_ERR("Invalid upper bounds for double precision. Digits after the decimal must be less than specified precision value. max: %g", max); + CLIENT_ERR("Invalid upper bounds for double precision. Digits after the decimal must be less than specified " + "precision value. max: %g", + max); return false; } if (scaled_min != trunc(scaled_min)) { - CLIENT_ERR("Invalid lower bounds for double precision. Digits after the decimal must be less than specified precision value. min: %g", min); + CLIENT_ERR("Invalid lower bounds for double precision. Digits after the decimal must be less than specified " + "precision value. min: %g", + min); return false; } if (fabs(scaled_max) >= SCALED_DOUBLE_BOUNDS) { - CLIENT_ERR("Invalid upper bounds for double precision. abs(max) must be less than 9223372036854775807. max: %g", max); + CLIENT_ERR("Invalid upper bounds for double precision. abs(max) must be less than 9223372036854775807. max: %g", + max); return false; } if (fabs(scaled_min) >= SCALED_DOUBLE_BOUNDS) { - CLIENT_ERR("Invalid lower bounds for double precision. abs(min) must be less than 9223372036854775807. min: %g", min); + CLIENT_ERR("Invalid lower bounds for double precision. abs(min) must be less than 9223372036854775807. min: %g", + min); return false; } @@ -229,19 +239,22 @@ bool mc_canUsePrecisionModeDouble(double min, double max, uint32_t precision, ui const double t_4 = UINT_64_MAX - t_1; const double t_5 = floor(log10(t_4)) - 1; - if ((double) precision >= t_5) { + if ((double)precision >= t_5) { CLIENT_ERR("Invalid value for precision. precision: %" PRId32, precision); return false; } - const int64_t i_1 = (int64_t) (scaled_max); - const int64_t i_2 = (int64_t) (scaled_min); + const int64_t i_1 = (int64_t)(scaled_max); + const int64_t i_2 = (int64_t)(scaled_min); const uint64_t range = subtract_int64_t(i_1, i_2); - const uint64_t i_3 = range + (uint64_t) (scaled_prc); + const uint64_t i_3 = range + (uint64_t)(scaled_prc); if (i_3 <= 0) { - CLIENT_ERR("Invalid value for upper and lower bounds for double precision. Min must be less than max. min: %g, max: %g", min, max); + CLIENT_ERR("Invalid value for upper and lower bounds for double precision. Min must be less than max. min: %g, " + "max: %g", + min, + max); return false; } @@ -331,7 +344,6 @@ bool mc_getTypeInfoDouble(mc_getTypeInfoDouble_args_t args, // If we are not in range_v2, then we don't care about the error returned // from canUsePrecisionMode so we can reset the status. _mongocrypt_status_reset(status); - } if (use_precision_mode) { @@ -425,7 +437,7 @@ static mlib_int128 dec128_to_uint128(mc_dec128 dec) { static mlib_int128 dec128_to_int128(mc_dec128 dec) { BSON_ASSERT(mc_dec128_less(dec, SIGNED_INT_128_MAX_DECIMAL)); - + bool negative = false; if (mc_dec128_is_negative(dec)) { @@ -448,7 +460,7 @@ bool ceil_log2_int128(mlib_int128 i, uint32_t *maxBitsOut, mongocrypt_status_t * return false; } - uint32_t clz = (uint32_t) _mlibCountLeadingZeros_u128(i); + uint32_t clz = (uint32_t)_mlibCountLeadingZeros_u128(i); uint32_t bits; // if i & (i - 1) == 0 @@ -461,7 +473,11 @@ bool ceil_log2_int128(mlib_int128 i, uint32_t *maxBitsOut, mongocrypt_status_t * return true; } -bool mc_canUsePrecisionModeDecimal(mc_dec128 min, mc_dec128 max, uint32_t precision, uint32_t *maxBitsOut, mongocrypt_status_t *status) { +bool mc_canUsePrecisionModeDecimal(mc_dec128 min, + mc_dec128 max, + uint32_t precision, + uint32_t *maxBitsOut, + mongocrypt_status_t *status) { BSON_ASSERT_PARAM(maxBitsOut); if (!mc_dec128_is_finite(max)) { @@ -475,7 +491,10 @@ bool mc_canUsePrecisionModeDecimal(mc_dec128 min, mc_dec128 max, uint32_t precis } if (mc_dec128_greater_equal(min, max)) { - CLIENT_ERR("Invalid upper and lower bounds for Decimal128 precision. Min must be strictly less than max. min: %s, max: %s", mc_dec128_to_string(min).str, mc_dec128_to_string(max).str); + CLIENT_ERR("Invalid upper and lower bounds for Decimal128 precision. Min must be strictly less than max. min: " + "%s, max: %s", + mc_dec128_to_string(min).str, + mc_dec128_to_string(max).str); return false; } @@ -486,35 +505,45 @@ bool mc_canUsePrecisionModeDecimal(mc_dec128 min, mc_dec128 max, uint32_t precis mc_dec128 scaled_min_trunc = mc_dec128_round_integral_ex(scaled_min, MC_DEC128_ROUND_TOWARD_ZERO, NULL); if (mc_dec128_not_equal(scaled_max, scaled_max_trunc)) { - CLIENT_ERR("Invalid upper bounds for Decimal128 precision. Digits after the decimal must be less than specified precision value. max: %s, precision: %" PRIu32, mc_dec128_to_string(max).str, precision); + CLIENT_ERR("Invalid upper bounds for Decimal128 precision. Digits after the decimal must be less than " + "specified precision value. max: %s, precision: %" PRIu32, + mc_dec128_to_string(max).str, + precision); return false; } if (mc_dec128_not_equal(scaled_min, scaled_min_trunc)) { - CLIENT_ERR("Invalid lower bounds for Decimal128 precision. Digits after the decimal must be less than specified precision value. min: %s, precision: %" PRIu32, mc_dec128_to_string(min).str, precision); + CLIENT_ERR("Invalid lower bounds for Decimal128 precision. Digits after the decimal must be less than " + "specified precision value. min: %s, precision: %" PRIu32, + mc_dec128_to_string(min).str, + precision); return false; } if (mc_dec128_greater(mc_dec128_abs(scaled_max), SIGNED_INT_128_MAX_DECIMAL)) { - CLIENT_ERR("Invalid upper bounds for Decimal128 precision, absolute scaled value must be less than 170141183460469231731687303715884105727. max: %s", mc_dec128_to_string(max).str); + CLIENT_ERR("Invalid upper bounds for Decimal128 precision, absolute scaled value must be less than " + "170141183460469231731687303715884105727. max: %s", + mc_dec128_to_string(max).str); return false; } if (mc_dec128_greater(mc_dec128_abs(scaled_min), SIGNED_INT_128_MAX_DECIMAL)) { - CLIENT_ERR("Invalid lower bounds for Decimal128 precision, absolute scaled value must be less than 170141183460469231731687303715884105727. min: %s", mc_dec128_to_string(min).str); + CLIENT_ERR("Invalid lower bounds for Decimal128 precision, absolute scaled value must be less than " + "170141183460469231731687303715884105727. min: %s", + mc_dec128_to_string(min).str); return false; } mc_dec128 t_1 = mc_dec128_sub(scaled_max, scaled_min); mc_dec128 t_4 = mc_dec128_sub(UNSIGNED_INT_128_MAX_DECIMAL, t_1); - mc_dec128 t_5 = mc_dec128_sub(mc_dec128_round_integral_ex(mc_dec128_log10(t_4), MC_DEC128_ROUND_UPWARD, NULL), MC_DEC128(1)); + mc_dec128 t_5 = + mc_dec128_sub(mc_dec128_round_integral_ex(mc_dec128_log10(t_4), MC_DEC128_ROUND_UPWARD, NULL), MC_DEC128(1)); if (mc_dec128_less(t_5, MC_DEC128(precision))) { CLIENT_ERR("Invalid value for precision. precision: %" PRIu32, precision); return false; } - mlib_int128 i_1 = dec128_to_int128(scaled_max); mlib_int128 i_2 = dec128_to_int128(scaled_min); @@ -528,7 +557,7 @@ bool mc_canUsePrecisionModeDecimal(mc_dec128 min, mc_dec128 max, uint32_t precis return false; } - mlib_int128 i_3 = mlib_int128_add(range128, mlib_int128_pow10((uint8_t) precision)); + mlib_int128 i_3 = mlib_int128_add(range128, mlib_int128_pow10((uint8_t)precision)); if (!ceil_log2_int128(i_3, maxBitsOut, status)) { return false; } diff --git a/test/test-mc-range-encoding.c b/test/test-mc-range-encoding.c index 8cec2a3f3..62df491ee 100644 --- a/test/test-mc-range-encoding.c +++ b/test/test-mc-range-encoding.c @@ -193,36 +193,30 @@ static void _test_RangeTest_Encode_Int64(_mongocrypt_tester_t *tester) { } } -#define INT_64_MAX_DOUBLE (double) 18446744073709551615ull +#define INT_64_MAX_DOUBLE (double)18446744073709551615ull static void _test_canUsePrecisionModeDouble(_mongocrypt_tester_t *tester) { -#define CAN_USE_PRECISION_MODE(lb, ub, prc, expected, expected_bits_out) \ - { \ - uint32_t bits_out = 0; \ - mongocrypt_status_t *const status = mongocrypt_status_new(); \ - printf("_test_canUsePrecisionModeDecimal, min: %f, max: %f, prc: %" PRIu32, \ - lb, \ - ub, \ - prc); \ - bool result = mc_canUsePrecisionModeDouble(lb, ub, prc, &bits_out, status); \ - ASSERT_OK_STATUS(mongocrypt_status_ok(status), status); \ - ASSERT(result == expected); \ - ASSERT_CMPINT32(expected_bits_out, ==, bits_out); \ - mongocrypt_status_destroy(status); \ +#define CAN_USE_PRECISION_MODE(lb, ub, prc, expected, expected_bits_out) \ + { \ + uint32_t bits_out = 0; \ + mongocrypt_status_t *const status = mongocrypt_status_new(); \ + printf("_test_canUsePrecisionModeDecimal, min: %f, max: %f, prc: %" PRIu32, lb, ub, prc); \ + bool result = mc_canUsePrecisionModeDouble(lb, ub, prc, &bits_out, status); \ + ASSERT_OK_STATUS(mongocrypt_status_ok(status), status); \ + ASSERT(result == expected); \ + ASSERT_CMPINT32(expected_bits_out, ==, bits_out); \ + mongocrypt_status_destroy(status); \ } -#define CAN_USE_PRECISION_MODE_ERRORS(lb, ub, prc, error) \ - { \ - mongocrypt_status_t *const status = mongocrypt_status_new(); \ - printf("_test_canUsePrecisionModeDecimal errors, min: %f, max: %f, prc: %" PRIu32, \ - lb, \ - ub, \ - prc); \ - uint32_t bits_out = 0; \ - bool result = mc_canUsePrecisionModeDouble(lb, ub, prc, &bits_out, status); \ - ASSERT_OR_PRINT_MSG(!result, "expected error, but got none"); \ - ASSERT_STATUS_CONTAINS(status, error); \ - mongocrypt_status_destroy(status); \ +#define CAN_USE_PRECISION_MODE_ERRORS(lb, ub, prc, error) \ + { \ + mongocrypt_status_t *const status = mongocrypt_status_new(); \ + printf("_test_canUsePrecisionModeDecimal errors, min: %f, max: %f, prc: %" PRIu32, lb, ub, prc); \ + uint32_t bits_out = 0; \ + bool result = mc_canUsePrecisionModeDouble(lb, ub, prc, &bits_out, status); \ + ASSERT_OR_PRINT_MSG(!result, "expected error, but got none"); \ + ASSERT_STATUS_CONTAINS(status, error); \ + mongocrypt_status_destroy(status); \ } CAN_USE_PRECISION_MODE(1.0, 16.0, 0, true, 4); @@ -239,17 +233,20 @@ static void _test_canUsePrecisionModeDouble(_mongocrypt_tester_t *tester) { CAN_USE_PRECISION_MODE_ERRORS(2.710000, 314.150000, 2, "Invalid upper bounds for double precision. Digits after"); CAN_USE_PRECISION_MODE_ERRORS(314.150000, 350.0, 2, "Invalid lower bounds for double precision. Digits after"); - CAN_USE_PRECISION_MODE_ERRORS( - (double) 9007199254740992, INT_64_MAX_DOUBLE, 0, "Invalid upper bounds for double precision. abs(max) must"); - CAN_USE_PRECISION_MODE_ERRORS( - -1 * INT_64_MAX_DOUBLE, -1 * (double) 9007199254740992, 0, "Invalid lower bounds for double precision. abs(min) must"); + CAN_USE_PRECISION_MODE_ERRORS((double)9007199254740992, + INT_64_MAX_DOUBLE, + 0, + "Invalid upper bounds for double precision. abs(max) must"); + CAN_USE_PRECISION_MODE_ERRORS(-1 * INT_64_MAX_DOUBLE, + -1 * (double)9007199254740992, + 0, + "Invalid lower bounds for double precision. abs(min) must"); CAN_USE_PRECISION_MODE_ERRORS(-92233720368547.0, 92233720368547.0, 5, "Invalid value for precision."); #undef CAN_USE_PRECISION_MODE #undef CAN_USE_PRECISION_MODE_ERRORS } - typedef struct { double value; mc_optional_double_t min; @@ -534,33 +531,33 @@ typedef struct { } Decimal128Test; static void _test_canUsePrecisionModeDecimal(_mongocrypt_tester_t *tester) { -#define CAN_USE_PRECISION_MODE(lb, ub, prc, expected, expected_bits_out) \ - { \ - uint32_t bits_out = 0; \ - mongocrypt_status_t *const status = mongocrypt_status_new(); \ - printf("_test_canUsePrecisionModeDecimal, min: %s, max: %s, prc: %" PRIu32, \ - mc_dec128_to_string(lb).str, \ - mc_dec128_to_string(ub).str, \ - prc); \ - bool result = mc_canUsePrecisionModeDecimal(lb, ub, prc, &bits_out, status); \ - ASSERT_OK_STATUS(mongocrypt_status_ok(status), status); \ - ASSERT(result == expected); \ - ASSERT_CMPINT32(expected_bits_out, ==, bits_out); \ - mongocrypt_status_destroy(status); \ +#define CAN_USE_PRECISION_MODE(lb, ub, prc, expected, expected_bits_out) \ + { \ + uint32_t bits_out = 0; \ + mongocrypt_status_t *const status = mongocrypt_status_new(); \ + printf("_test_canUsePrecisionModeDecimal, min: %s, max: %s, prc: %" PRIu32, \ + mc_dec128_to_string(lb).str, \ + mc_dec128_to_string(ub).str, \ + prc); \ + bool result = mc_canUsePrecisionModeDecimal(lb, ub, prc, &bits_out, status); \ + ASSERT_OK_STATUS(mongocrypt_status_ok(status), status); \ + ASSERT(result == expected); \ + ASSERT_CMPINT32(expected_bits_out, ==, bits_out); \ + mongocrypt_status_destroy(status); \ } -#define CAN_USE_PRECISION_MODE_ERRORS(lb, ub, prc, error) \ - { \ - mongocrypt_status_t *const status = mongocrypt_status_new(); \ - printf("_test_canUsePrecisionModeDecimal errors, min: %s, max: %s, prc: %" PRIu32, \ - mc_dec128_to_string(lb).str, \ - mc_dec128_to_string(ub).str, \ - prc); \ - uint32_t bits_out = 0; \ - bool result = mc_canUsePrecisionModeDecimal(lb, ub, prc, &bits_out, status); \ - ASSERT_OR_PRINT_MSG(!result, "expected error, but got none"); \ - ASSERT_STATUS_CONTAINS(status, error); \ - mongocrypt_status_destroy(status); \ +#define CAN_USE_PRECISION_MODE_ERRORS(lb, ub, prc, error) \ + { \ + mongocrypt_status_t *const status = mongocrypt_status_new(); \ + printf("_test_canUsePrecisionModeDecimal errors, min: %s, max: %s, prc: %" PRIu32, \ + mc_dec128_to_string(lb).str, \ + mc_dec128_to_string(ub).str, \ + prc); \ + uint32_t bits_out = 0; \ + bool result = mc_canUsePrecisionModeDecimal(lb, ub, prc, &bits_out, status); \ + ASSERT_OR_PRINT_MSG(!result, "expected error, but got none"); \ + ASSERT_STATUS_CONTAINS(status, error); \ + mongocrypt_status_destroy(status); \ } CAN_USE_PRECISION_MODE(MC_DEC128(1), MC_DEC128(16), 0, true, 4); @@ -568,10 +565,8 @@ static void _test_canUsePrecisionModeDecimal(_mongocrypt_tester_t *tester) { // It is unclear where Decimal128 looses precision, so we choose an arbitrarily large value // and make sure that max_bits is correct for that boundary. - CAN_USE_PRECISION_MODE( - MC_DEC128(1), mc_dec128_from_string("324518553658426726783156020576256"), 0, true, 108); - CAN_USE_PRECISION_MODE( - MC_DEC128(0), mc_dec128_from_string("324518553658426726783156020576256"), 0, true, 109); + CAN_USE_PRECISION_MODE(MC_DEC128(1), mc_dec128_from_string("324518553658426726783156020576256"), 0, true, 108); + CAN_USE_PRECISION_MODE(MC_DEC128(0), mc_dec128_from_string("324518553658426726783156020576256"), 0, true, 109); CAN_USE_PRECISION_MODE(mc_dec128_from_string("-100000000000000000000000000000000"), mc_dec128_from_string("170141183460469231731687303715880000000"), @@ -579,10 +574,14 @@ static void _test_canUsePrecisionModeDecimal(_mongocrypt_tester_t *tester) { false, 128); - CAN_USE_PRECISION_MODE_ERRORS( - mc_dec128_from_string("788545.12392843"), mc_dec128_from_string("4607431769000000.129834923"), 4, "Invalid upper bounds for Decimal128 precision. Digits after"); - CAN_USE_PRECISION_MODE_ERRORS( - mc_dec128_from_string("788545.12392843"), mc_dec128_from_string("7885451.2"), 4, "Invalid lower bounds for Decimal128 precision. Digits after"); + CAN_USE_PRECISION_MODE_ERRORS(mc_dec128_from_string("788545.12392843"), + mc_dec128_from_string("4607431769000000.129834923"), + 4, + "Invalid upper bounds for Decimal128 precision. Digits after"); + CAN_USE_PRECISION_MODE_ERRORS(mc_dec128_from_string("788545.12392843"), + mc_dec128_from_string("7885451.2"), + 4, + "Invalid lower bounds for Decimal128 precision. Digits after"); CAN_USE_PRECISION_MODE_ERRORS(mc_dec128_from_string("324518553658426726783156020576256"), mc_dec128_from_string("340282366920938463463374607431768211455"), 10, @@ -812,11 +811,9 @@ static void _test_RangeTest_Encode_Decimal128(_mongocrypt_tester_t *tester) { #undef ASSERT_EIBP #define ASSERT_EIBB(Val, Max, Min, Precision, Expect) \ - (Decimal128Test){ \ - .value = mc_dec128_from_string(#Val), \ - .min = OPT_MC_DEC128(mc_dec128_from_string(#Min)), \ - .max = OPT_MC_DEC128(mc_dec128_from_string(#Max)), \ - .precision = OPT_U32(Precision), \ + (Decimal128Test) { \ + .value = mc_dec128_from_string(#Val), .min = OPT_MC_DEC128(mc_dec128_from_string(#Min)), \ + .max = OPT_MC_DEC128(mc_dec128_from_string(#Max)), .precision = OPT_U32(Precision), \ .expect = MLIB_INT128_CAST(Expect), \ } @@ -848,10 +845,9 @@ static void _test_RangeTest_Encode_Decimal128(_mongocrypt_tester_t *tester) { .expect = Expect, \ .use_range_v1 = true, \ }, \ - (Decimal128Test) { \ + (Decimal128Test) { \ .value = mc_dec128_from_string(#Val), .min = OPT_MC_DEC128(mc_dec128_from_string(#Min)), \ - .max = OPT_MC_DEC128(mc_dec128_from_string(#Max)), .precision = OPT_U32(Precision), \ - .expectError = Error \ + .max = OPT_MC_DEC128(mc_dec128_from_string(#Max)), .precision = OPT_U32(Precision), .expectError = Error \ } // ASSERT_EIBB(0, 1, -1, 3, 1000), @@ -900,9 +896,10 @@ static void _test_RangeTest_Encode_Decimal128(_mongocrypt_tester_t *tester) { // Test a range that requires > 64 bits. ASSERT_EIBB(5, 18446744073709551616, .1, 1, 49), - // // Test a range that requires > 64 bits. - // // min has more places after the decimal than precision. - // ASSERT_EIBB_ERROR(5, 18446744073709551616, .01, 1, 49, "Invalid lower bounds for Decimal128 precision. Digits after the decimal"), + // // Test a range that requires > 64 bits. + // // min has more places after the decimal than precision. + // ASSERT_EIBB_ERROR(5, 18446744073709551616, .01, 1, 49, "Invalid lower bounds for Decimal128 precision. Digits + // after the decimal"), #undef ASSERT_EIBB #undef ASSERT_EIBB_OVERFLOW From 89f26849141cfad95fec930c9da4ac7a3a932cb1 Mon Sep 17 00:00:00 2001 From: Shreyas Kalyan Date: Thu, 18 Jul 2024 15:41:26 -0400 Subject: [PATCH 03/11] fix errors in build bson --- src/mc-range-encoding.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mc-range-encoding.c b/src/mc-range-encoding.c index a1343ecdd..d7a590b90 100644 --- a/src/mc-range-encoding.c +++ b/src/mc-range-encoding.c @@ -166,7 +166,7 @@ bool mc_getTypeInfo64(mc_getTypeInfo64_args_t args, mc_OSTType_Int64 *out, mongo #define UINT_64_MAX 18446744073709551615ull uint64_t subtract_int64_t(int64_t max, int64_t min) { - if (max > 0 && min > 0 || max < 0 && min < 0) { + if ((max > 0 && min > 0) || (max < 0 && min < 0)) { return (uint64_t)(max - min); } @@ -236,7 +236,7 @@ bool mc_canUsePrecisionModeDouble(double min, } const double t_1 = scaled_max - scaled_min; - const double t_4 = UINT_64_MAX - t_1; + const double t_4 = (double) UINT_64_MAX - t_1; const double t_5 = floor(log10(t_4)) - 1; if ((double)precision >= t_5) { From ef98c4c52e1328e25e8d1ed6c5b01bb4ab89260c Mon Sep 17 00:00:00 2001 From: Shreyas Kalyan Date: Thu, 18 Jul 2024 15:45:25 -0400 Subject: [PATCH 04/11] lint --- src/mc-range-encoding.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mc-range-encoding.c b/src/mc-range-encoding.c index d7a590b90..5fe74fd9b 100644 --- a/src/mc-range-encoding.c +++ b/src/mc-range-encoding.c @@ -236,7 +236,7 @@ bool mc_canUsePrecisionModeDouble(double min, } const double t_1 = scaled_max - scaled_min; - const double t_4 = (double) UINT_64_MAX - t_1; + const double t_4 = (double)UINT_64_MAX - t_1; const double t_5 = floor(log10(t_4)) - 1; if ((double)precision >= t_5) { From 669f399f00f4d1546dc4cb62942986b65e3728de Mon Sep 17 00:00:00 2001 From: Shreyas Kalyan Date: Thu, 18 Jul 2024 16:21:00 -0400 Subject: [PATCH 05/11] fix some windows errors and lint --- .evergreen/init.sh | 1 + src/mc-range-encoding.c | 9 +++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.evergreen/init.sh b/.evergreen/init.sh index 864237881..51ec07b85 100644 --- a/.evergreen/init.sh +++ b/.evergreen/init.sh @@ -222,6 +222,7 @@ run_ctest() { run_python() { pys=( + /Users/shreyaskal/dev/bin/python py python3.14 python3.13 diff --git a/src/mc-range-encoding.c b/src/mc-range-encoding.c index 5fe74fd9b..4b9a36130 100644 --- a/src/mc-range-encoding.c +++ b/src/mc-range-encoding.c @@ -166,12 +166,17 @@ bool mc_getTypeInfo64(mc_getTypeInfo64_args_t args, mc_OSTType_Int64 *out, mongo #define UINT_64_MAX 18446744073709551615ull uint64_t subtract_int64_t(int64_t max, int64_t min) { + // If the values have the same sign, then simple subtraction + // will work because we know max > min. if ((max > 0 && min > 0) || (max < 0 && min < 0)) { return (uint64_t)(max - min); } - uint64_t u_return = (uint64_t)labs(max); - u_return += (uint64_t)labs(min); + // If they are opposite signs, then we can just do a small + // abs_val addition trick to perform subtraction, since it + // is just the sum of differences between the values and 0. + uint64_t u_return = (uint64_t)llabs(max); + u_return += (uint64_t)llabs(min); return u_return; } From b702e196bfc93170faed164c090749a8fb553e9c Mon Sep 17 00:00:00 2001 From: Shreyas Kalyan Date: Mon, 22 Jul 2024 13:10:00 -0400 Subject: [PATCH 06/11] fix erwin's comments --- .evergreen/init.sh | 1 - src/mc-range-encoding-private.h | 1 - src/mc-range-encoding.c | 63 ++++++++++++++++++--------------- test/test-mc-range-encoding.c | 49 +++++++++++++------------ 4 files changed, 58 insertions(+), 56 deletions(-) diff --git a/.evergreen/init.sh b/.evergreen/init.sh index 51ec07b85..864237881 100644 --- a/.evergreen/init.sh +++ b/.evergreen/init.sh @@ -222,7 +222,6 @@ run_ctest() { run_python() { pys=( - /Users/shreyaskal/dev/bin/python py python3.14 python3.13 diff --git a/src/mc-range-encoding-private.h b/src/mc-range-encoding-private.h index b1ab0d76b..fb6180dfa 100644 --- a/src/mc-range-encoding-private.h +++ b/src/mc-range-encoding-private.h @@ -20,7 +20,6 @@ #include "mc-dec128.h" #include "mc-optional-private.h" #include "mongocrypt-status-private.h" -#include "mongocrypt.h" #include diff --git a/src/mc-range-encoding.c b/src/mc-range-encoding.c index 4b9a36130..50f23945a 100644 --- a/src/mc-range-encoding.c +++ b/src/mc-range-encoding.c @@ -163,20 +163,19 @@ bool mc_getTypeInfo64(mc_getTypeInfo64_args_t args, mc_OSTType_Int64 *out, mongo #define exp10Double(x) pow(10, x) #define SCALED_DOUBLE_BOUNDS 9223372036854775807.0 // 2^63 - 1 -#define UINT_64_MAX 18446744073709551615ull uint64_t subtract_int64_t(int64_t max, int64_t min) { + BSON_ASSERT(max > min); // If the values have the same sign, then simple subtraction // will work because we know max > min. if ((max > 0 && min > 0) || (max < 0 && min < 0)) { return (uint64_t)(max - min); } - // If they are opposite signs, then we can just do a small - // abs_val addition trick to perform subtraction, since it - // is just the sum of differences between the values and 0. - uint64_t u_return = (uint64_t)llabs(max); - u_return += (uint64_t)llabs(min); + // If they are opposite signs, then we can just invert + // min to be positive and return the sum. + uint64_t u_return = (uint64_t)max; + u_return += (uint64_t)(~min + 1); return u_return; } @@ -204,8 +203,8 @@ bool mc_canUsePrecisionModeDouble(double min, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(maxBitsOut); - if (min > max) { - CLIENT_ERR("Invalid bounds for double range precision, max must be less than min. min: %g, max: %g", min, max); + if (min >= max) { + CLIENT_ERR("Invalid bounds for double range precision, min must be less than max. min: %g, max: %g", min, max); return false; } @@ -215,36 +214,38 @@ bool mc_canUsePrecisionModeDouble(double min, const double scaled_min = min * scaled_prc; if (scaled_max != trunc(scaled_max)) { - CLIENT_ERR("Invalid upper bounds for double precision. Digits after the decimal must be less than specified " + CLIENT_ERR("Invalid upper bound for double precision. Fractional digits must be less than the specified " "precision value. max: %g", max); return false; } if (scaled_min != trunc(scaled_min)) { - CLIENT_ERR("Invalid lower bounds for double precision. Digits after the decimal must be less than specified " + CLIENT_ERR("Invalid lower bound for double precision. Fractional digits must be less than the specified " "precision value. min: %g", min); return false; } if (fabs(scaled_max) >= SCALED_DOUBLE_BOUNDS) { - CLIENT_ERR("Invalid upper bounds for double precision. abs(max) must be less than 9223372036854775807. max: %g", + CLIENT_ERR("Invalid upper bound for double precision. Absolute scaled value of max must be less than %g. max: %g", + SCALED_DOUBLE_BOUNDS, max); return false; } if (fabs(scaled_min) >= SCALED_DOUBLE_BOUNDS) { - CLIENT_ERR("Invalid lower bounds for double precision. abs(min) must be less than 9223372036854775807. min: %g", + CLIENT_ERR("Invalid lower bound for double precision. Absolute scaled value of min must be less than %g. min: %g", + SCALED_DOUBLE_BOUNDS, min); return false; } const double t_1 = scaled_max - scaled_min; - const double t_4 = (double)UINT_64_MAX - t_1; + const double t_4 = (double)UINT64_MAX - t_1; const double t_5 = floor(log10(t_4)) - 1; - if ((double)precision >= t_5) { + if ((double)precision > t_5) { CLIENT_ERR("Invalid value for precision. precision: %" PRId32, precision); return false; } @@ -253,16 +254,18 @@ bool mc_canUsePrecisionModeDouble(double min, const int64_t i_2 = (int64_t)(scaled_min); const uint64_t range = subtract_int64_t(i_1, i_2); - const uint64_t i_3 = range + (uint64_t)(scaled_prc); - if (i_3 <= 0) { - CLIENT_ERR("Invalid value for upper and lower bounds for double precision. Min must be less than max. min: %g, " - "max: %g", + if (((uint64_t)scaled_prc) > UINT64_MAX - range) { + CLIENT_ERR("Invalid value for min, max, and precision. The calculated domain size is too large. min: %g, max: " + "%g, precision: %" PRIu32, min, - max); + max, + precision); return false; } + const uint64_t i_3 = range + (uint64_t)(scaled_prc); + if (!ceil_log2_double(i_3, maxBitsOut, status)) { return false; } @@ -486,12 +489,12 @@ bool mc_canUsePrecisionModeDecimal(mc_dec128 min, BSON_ASSERT_PARAM(maxBitsOut); if (!mc_dec128_is_finite(max)) { - CLIENT_ERR("Invalid upper bounds for Decimal128 precision. Max is infinite."); + CLIENT_ERR("Invalid upper bound for Decimal128 precision. Max is infinite."); return false; } if (!mc_dec128_is_finite(min)) { - CLIENT_ERR("Invalid lower bounds for Decimal128 precision. Min is infinite."); + CLIENT_ERR("Invalid lower bound for Decimal128 precision. Min is infinite."); return false; } @@ -510,31 +513,33 @@ bool mc_canUsePrecisionModeDecimal(mc_dec128 min, mc_dec128 scaled_min_trunc = mc_dec128_round_integral_ex(scaled_min, MC_DEC128_ROUND_TOWARD_ZERO, NULL); if (mc_dec128_not_equal(scaled_max, scaled_max_trunc)) { - CLIENT_ERR("Invalid upper bounds for Decimal128 precision. Digits after the decimal must be less than " - "specified precision value. max: %s, precision: %" PRIu32, + CLIENT_ERR("Invalid upper bound for Decimal128 precision. Fractional digits must be less than " + "the specified precision value. max: %s, precision: %" PRIu32, mc_dec128_to_string(max).str, precision); return false; } if (mc_dec128_not_equal(scaled_min, scaled_min_trunc)) { - CLIENT_ERR("Invalid lower bounds for Decimal128 precision. Digits after the decimal must be less than " - "specified precision value. min: %s, precision: %" PRIu32, + CLIENT_ERR("Invalid lower bound for Decimal128 precision. Fractional digits must be less than " + "the specified precision value. min: %s, precision: %" PRIu32, mc_dec128_to_string(min).str, precision); return false; } if (mc_dec128_greater(mc_dec128_abs(scaled_max), SIGNED_INT_128_MAX_DECIMAL)) { - CLIENT_ERR("Invalid upper bounds for Decimal128 precision, absolute scaled value must be less than " - "170141183460469231731687303715884105727. max: %s", + CLIENT_ERR("Invalid upper bound for Decimal128 precision. Absolute scaled value must be less than " + "%s. max: %s", + mc_dec128_to_string(SIGNED_INT_128_MAX_DECIMAL).str, mc_dec128_to_string(max).str); return false; } if (mc_dec128_greater(mc_dec128_abs(scaled_min), SIGNED_INT_128_MAX_DECIMAL)) { - CLIENT_ERR("Invalid lower bounds for Decimal128 precision, absolute scaled value must be less than " - "170141183460469231731687303715884105727. min: %s", + CLIENT_ERR("Invalid lower bound for Decimal128 precision. Absolute scaled value must be less than " + "%s. min: %s", + mc_dec128_to_string(SIGNED_INT_128_MAX_DECIMAL).str, mc_dec128_to_string(min).str); return false; } diff --git a/test/test-mc-range-encoding.c b/test/test-mc-range-encoding.c index 62df491ee..3b58794cb 100644 --- a/test/test-mc-range-encoding.c +++ b/test/test-mc-range-encoding.c @@ -230,17 +230,20 @@ static void _test_canUsePrecisionModeDouble(_mongocrypt_tester_t *tester) { CAN_USE_PRECISION_MODE(-1000000000.0, 9223372036844775424.0, 0, false, 64); - CAN_USE_PRECISION_MODE_ERRORS(2.710000, 314.150000, 2, "Invalid upper bounds for double precision. Digits after"); - CAN_USE_PRECISION_MODE_ERRORS(314.150000, 350.0, 2, "Invalid lower bounds for double precision. Digits after"); + CAN_USE_PRECISION_MODE_ERRORS(2.710000, + 314.150000, + 2, + "Invalid upper bound for double precision. Fractional digits"); + CAN_USE_PRECISION_MODE_ERRORS(314.150000, 350.0, 2, "Invalid lower bound for double precision. Fractional digits"); CAN_USE_PRECISION_MODE_ERRORS((double)9007199254740992, INT_64_MAX_DOUBLE, 0, - "Invalid upper bounds for double precision. abs(max) must"); + "Invalid upper bound for double precision. Absolute scaled value"); CAN_USE_PRECISION_MODE_ERRORS(-1 * INT_64_MAX_DOUBLE, -1 * (double)9007199254740992, 0, - "Invalid lower bounds for double precision. abs(min) must"); + "Invalid lower bound for double precision. Absolute scaled value"); CAN_USE_PRECISION_MODE_ERRORS(-92233720368547.0, 92233720368547.0, 5, "Invalid value for precision."); #undef CAN_USE_PRECISION_MODE @@ -374,14 +377,14 @@ static void _test_RangeTest_Encode_Double(_mongocrypt_tester_t *tester) { .precision = OPT_U32_C(3), // Applying min/max/precision result in a domain needing >= 64 bits to represent. // For range v2, expect an error. - .expectError = "Invalid upper bounds for double precision."}, + .expectError = "Invalid upper bound for double precision."}, {.value = 0, .max = OPT_DOUBLE_C(DBL_MAX), .min = OPT_DOUBLE_C(-DBL_MAX), .precision = OPT_U32_C(3), // Applying min/max/precision result in a domain needing >= 64 bits to represent. // For range v2, expect an error. - .expectError = "Invalid upper bounds for double precision."}, + .expectError = "Invalid upper bound for double precision."}, {.value = 3.141592653589, .max = OPT_DOUBLE_C(5), .min = OPT_DOUBLE_C(0), @@ -424,14 +427,14 @@ static void _test_RangeTest_Encode_Double(_mongocrypt_tester_t *tester) { .precision = OPT_U32_C(3), // Applying min/max/precision result in a domain needing >= 64 bits to represent. // For range v2, expect an error. - .expectError = "Invalid upper bounds for double precision."}, + .expectError = "Invalid upper bound for double precision."}, {.value = 1E100, .max = OPT_DOUBLE_C(DBL_MAX), .min = OPT_DOUBLE_C(-DBL_MAX), .precision = OPT_U32_C(3), // Applying min/max/precision result in a domain needing >= 64 bits to represent. // For range v2, expect an error. - .expectError = "Invalid upper bounds for double precision."}, + .expectError = "Invalid upper bound for double precision."}, {.value = 1E9, .max = OPT_DOUBLE_C(1E10), .min = OPT_DOUBLE_C(0), @@ -462,14 +465,14 @@ static void _test_RangeTest_Encode_Double(_mongocrypt_tester_t *tester) { .precision = OPT_U32_C(35), // Applying min/max/precision result in a domain needing >= 64 bits to represent. // For range v2, expect an error. - .expectError = "Invalid upper bounds for double precision."}, + .expectError = "Invalid upper bound for double precision."}, {.value = 1E-30, .max = OPT_DOUBLE_C(10E-30), .min = OPT_DOUBLE_C(1E-30), .precision = OPT_U32_C(35), // Applying min/max/precision result in a domain needing >= 64 bits to represent. // For range v2, expect an error. - .expectError = "Invalid upper bounds for double precision."}, + .expectError = "Invalid upper bound for double precision."}, /* Test cases copied from Double_Bounds_Precision ... end */ {.value = -1, .min = OPT_DOUBLE_C(0), @@ -577,20 +580,20 @@ static void _test_canUsePrecisionModeDecimal(_mongocrypt_tester_t *tester) { CAN_USE_PRECISION_MODE_ERRORS(mc_dec128_from_string("788545.12392843"), mc_dec128_from_string("4607431769000000.129834923"), 4, - "Invalid upper bounds for Decimal128 precision. Digits after"); + "Invalid upper bound for Decimal128 precision. Fractional digits"); CAN_USE_PRECISION_MODE_ERRORS(mc_dec128_from_string("788545.12392843"), mc_dec128_from_string("7885451.2"), 4, - "Invalid lower bounds for Decimal128 precision. Digits after"); + "Invalid lower bound for Decimal128 precision. Fractional digits"); CAN_USE_PRECISION_MODE_ERRORS(mc_dec128_from_string("324518553658426726783156020576256"), mc_dec128_from_string("340282366920938463463374607431768211455"), 10, - "Invalid upper bounds for Decimal128 precision, absolute scaled value must be"); + "Invalid upper bound for Decimal128 precision. Absolute scaled"); CAN_USE_PRECISION_MODE_ERRORS(mc_dec128_from_string("-340282366920938463463374607431768211455"), mc_dec128_from_string("-3245185536584267267831560"), 10, - "Invalid lower bounds for Decimal128 precision, absolute scaled value must be"); + "Invalid lower bound for Decimal128 precision. Absolute scaled"); CAN_USE_PRECISION_MODE_ERRORS(mc_dec128_from_string("-17014118346046923173168730371588.0000000"), mc_dec128_from_string("17014118346046923173168730371588.0000000"), @@ -600,7 +603,7 @@ static void _test_canUsePrecisionModeDecimal(_mongocrypt_tester_t *tester) { CAN_USE_PRECISION_MODE_ERRORS(MC_DEC128(788545.000000), mc_dec128_from_string("340282366920938463463374607431769000000.000000"), 0, - "Invalid upper bounds for Decimal128 precision, absolute scaled value must be"); + "Invalid upper bound for Decimal128 precision. Absolute scaled"); #undef CAN_USE_PRECISION_MODE #undef CAN_USE_PRECISION_MODE_ERRORS @@ -850,24 +853,24 @@ static void _test_RangeTest_Encode_Decimal128(_mongocrypt_tester_t *tester) { .max = OPT_MC_DEC128(mc_dec128_from_string(#Max)), .precision = OPT_U32(Precision), .expectError = Error \ } - // ASSERT_EIBB(0, 1, -1, 3, 1000), - // ASSERT_EIBB(0, 1, -1E5, 3, 100000000), + ASSERT_EIBB(0, 1, -1, 3, 1000), + ASSERT_EIBB(0, 1, -1E5, 3, 100000000), - // ASSERT_EIBB(-1E-33, 1, -1E5, 3, 100000000), + ASSERT_EIBB(-1E-33, 1, -1E5, 3, 100000000), ASSERT_EIBB_ERROR(0, MC_DEC128_LARGEST_POSITIVE, MC_DEC128_LARGEST_NEGATIVE, 3, mlib_int128_from_string("170141183460469231731687303715884105728", NULL), - "Invalid upper bounds for Decimal128 precision. Max is infinite."), + "Invalid upper bound for Decimal128 precision. Max is infinite."), ASSERT_EIBB_ERROR(0, DBL_MAX, DBL_MIN, 3, mlib_int128_from_string("170141183460469231731687303715884105728", NULL), - "Invalid upper bounds for Decimal128 precision. Max is infinite."), + "Invalid upper bound for Decimal128 precision. Max is infinite."), ASSERT_EIBB(3.141592653589, 5, 0, 0, 3), ASSERT_EIBB(3.141592653589, 5, 0, 1, 31), @@ -884,7 +887,7 @@ static void _test_RangeTest_Encode_Decimal128(_mongocrypt_tester_t *tester) { DBL_MIN, 3, mlib_int128_from_string("232572183460469231731687303715884099485", NULL), - "Invalid upper bounds for Decimal128 precision. Max is infinite."), + "Invalid upper bound for Decimal128 precision. Max is infinite."), ASSERT_EIBB(1E9, 1E10, 0, 3, 1000000000000), ASSERT_EIBB(1E9, 1E10, 0, 0, 1000000000), @@ -896,10 +899,6 @@ static void _test_RangeTest_Encode_Decimal128(_mongocrypt_tester_t *tester) { // Test a range that requires > 64 bits. ASSERT_EIBB(5, 18446744073709551616, .1, 1, 49), - // // Test a range that requires > 64 bits. - // // min has more places after the decimal than precision. - // ASSERT_EIBB_ERROR(5, 18446744073709551616, .01, 1, 49, "Invalid lower bounds for Decimal128 precision. Digits - // after the decimal"), #undef ASSERT_EIBB #undef ASSERT_EIBB_OVERFLOW From 9be2a2984922cff6ac1287b5d85b9d56dc98e9b2 Mon Sep 17 00:00:00 2001 From: Shreyas Kalyan Date: Mon, 22 Jul 2024 13:11:52 -0400 Subject: [PATCH 07/11] lint --- src/mc-range-encoding.c | 14 ++++++++------ test/test-mc-range-encoding.c | 8 +++++--- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/mc-range-encoding.c b/src/mc-range-encoding.c index 50f23945a..14c2fbf53 100644 --- a/src/mc-range-encoding.c +++ b/src/mc-range-encoding.c @@ -228,16 +228,18 @@ bool mc_canUsePrecisionModeDouble(double min, } if (fabs(scaled_max) >= SCALED_DOUBLE_BOUNDS) { - CLIENT_ERR("Invalid upper bound for double precision. Absolute scaled value of max must be less than %g. max: %g", - SCALED_DOUBLE_BOUNDS, - max); + CLIENT_ERR( + "Invalid upper bound for double precision. Absolute scaled value of max must be less than %g. max: %g", + SCALED_DOUBLE_BOUNDS, + max); return false; } if (fabs(scaled_min) >= SCALED_DOUBLE_BOUNDS) { - CLIENT_ERR("Invalid lower bound for double precision. Absolute scaled value of min must be less than %g. min: %g", - SCALED_DOUBLE_BOUNDS, - min); + CLIENT_ERR( + "Invalid lower bound for double precision. Absolute scaled value of min must be less than %g. min: %g", + SCALED_DOUBLE_BOUNDS, + min); return false; } diff --git a/test/test-mc-range-encoding.c b/test/test-mc-range-encoding.c index 3b58794cb..5a1a779c6 100644 --- a/test/test-mc-range-encoding.c +++ b/test/test-mc-range-encoding.c @@ -814,9 +814,11 @@ static void _test_RangeTest_Encode_Decimal128(_mongocrypt_tester_t *tester) { #undef ASSERT_EIBP #define ASSERT_EIBB(Val, Max, Min, Precision, Expect) \ - (Decimal128Test) { \ - .value = mc_dec128_from_string(#Val), .min = OPT_MC_DEC128(mc_dec128_from_string(#Min)), \ - .max = OPT_MC_DEC128(mc_dec128_from_string(#Max)), .precision = OPT_U32(Precision), \ + (Decimal128Test){ \ + .value = mc_dec128_from_string(#Val), \ + .min = OPT_MC_DEC128(mc_dec128_from_string(#Min)), \ + .max = OPT_MC_DEC128(mc_dec128_from_string(#Max)), \ + .precision = OPT_U32(Precision), \ .expect = MLIB_INT128_CAST(Expect), \ } From 3c4649b82b86353688f46fc7e719480fd86f2cb7 Mon Sep 17 00:00:00 2001 From: Shreyas Kalyan Date: Wed, 24 Jul 2024 09:11:49 -0400 Subject: [PATCH 08/11] addressed Erwin's comments --- src/mc-range-encoding.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/mc-range-encoding.c b/src/mc-range-encoding.c index 14c2fbf53..74454a0cb 100644 --- a/src/mc-range-encoding.c +++ b/src/mc-range-encoding.c @@ -532,7 +532,7 @@ bool mc_canUsePrecisionModeDecimal(mc_dec128 min, if (mc_dec128_greater(mc_dec128_abs(scaled_max), SIGNED_INT_128_MAX_DECIMAL)) { CLIENT_ERR("Invalid upper bound for Decimal128 precision. Absolute scaled value must be less than " - "%s. max: %s", + "or equal to %s. max: %s", mc_dec128_to_string(SIGNED_INT_128_MAX_DECIMAL).str, mc_dec128_to_string(max).str); return false; @@ -540,7 +540,7 @@ bool mc_canUsePrecisionModeDecimal(mc_dec128 min, if (mc_dec128_greater(mc_dec128_abs(scaled_min), SIGNED_INT_128_MAX_DECIMAL)) { CLIENT_ERR("Invalid lower bound for Decimal128 precision. Absolute scaled value must be less than " - "%s. min: %s", + "or equal to %s. min: %s", mc_dec128_to_string(SIGNED_INT_128_MAX_DECIMAL).str, mc_dec128_to_string(min).str); return false; @@ -549,7 +549,7 @@ bool mc_canUsePrecisionModeDecimal(mc_dec128 min, mc_dec128 t_1 = mc_dec128_sub(scaled_max, scaled_min); mc_dec128 t_4 = mc_dec128_sub(UNSIGNED_INT_128_MAX_DECIMAL, t_1); mc_dec128 t_5 = - mc_dec128_sub(mc_dec128_round_integral_ex(mc_dec128_log10(t_4), MC_DEC128_ROUND_UPWARD, NULL), MC_DEC128(1)); + mc_dec128_sub(mc_dec128_round_integral_ex(mc_dec128_log10(t_4), MC_DEC128_ROUND_TOWARD_ZERO, NULL), MC_DEC128(1)); if (mc_dec128_less(t_5, MC_DEC128(precision))) { CLIENT_ERR("Invalid value for precision. precision: %" PRIu32, precision); @@ -564,7 +564,7 @@ bool mc_canUsePrecisionModeDecimal(mc_dec128 min, // representation will yield the actual range result. mlib_int128 range128 = mlib_int128_sub(i_1, i_2); - if (precision > 255) { + if (precision > UINT8_MAX) { CLIENT_ERR("Invalid value for precision. Must be less than 255. precision: %" PRIu32, precision); return false; } From 3e5e3093d62590c3738062ac15ec5e4f61f14a8b Mon Sep 17 00:00:00 2001 From: Shreyas Kalyan Date: Wed, 24 Jul 2024 09:17:44 -0400 Subject: [PATCH 09/11] lint --- src/mc-range-encoding.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mc-range-encoding.c b/src/mc-range-encoding.c index 74454a0cb..394c23d73 100644 --- a/src/mc-range-encoding.c +++ b/src/mc-range-encoding.c @@ -548,8 +548,8 @@ bool mc_canUsePrecisionModeDecimal(mc_dec128 min, mc_dec128 t_1 = mc_dec128_sub(scaled_max, scaled_min); mc_dec128 t_4 = mc_dec128_sub(UNSIGNED_INT_128_MAX_DECIMAL, t_1); - mc_dec128 t_5 = - mc_dec128_sub(mc_dec128_round_integral_ex(mc_dec128_log10(t_4), MC_DEC128_ROUND_TOWARD_ZERO, NULL), MC_DEC128(1)); + mc_dec128 t_5 = mc_dec128_sub(mc_dec128_round_integral_ex(mc_dec128_log10(t_4), MC_DEC128_ROUND_TOWARD_ZERO, NULL), + MC_DEC128(1)); if (mc_dec128_less(t_5, MC_DEC128(precision))) { CLIENT_ERR("Invalid value for precision. precision: %" PRIu32, precision); From 6d540f88cbf2a3c946f1c837cc6961dce10c66ae Mon Sep 17 00:00:00 2001 From: Shreyas Kalyan Date: Wed, 24 Jul 2024 10:33:41 -0400 Subject: [PATCH 10/11] fixed windows issue --- src/mc-range-encoding.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/mc-range-encoding.c b/src/mc-range-encoding.c index 394c23d73..c2408635b 100644 --- a/src/mc-range-encoding.c +++ b/src/mc-range-encoding.c @@ -551,7 +551,10 @@ bool mc_canUsePrecisionModeDecimal(mc_dec128 min, mc_dec128 t_5 = mc_dec128_sub(mc_dec128_round_integral_ex(mc_dec128_log10(t_4), MC_DEC128_ROUND_TOWARD_ZERO, NULL), MC_DEC128(1)); - if (mc_dec128_less(t_5, MC_DEC128(precision))) { + // We convert precision to a double so we can avoid warning C4146 on Windows. + mc_dec128 prc_dec = mc_dec128_from_double((double)precision); + + if (mc_dec128_less(t_5, prc_dec)) { CLIENT_ERR("Invalid value for precision. precision: %" PRIu32, precision); return false; } From 8682f57fd1446961f08bfcb531a0a05b59194e55 Mon Sep 17 00:00:00 2001 From: Shreyas Kalyan Date: Wed, 24 Jul 2024 14:13:13 -0400 Subject: [PATCH 11/11] addressed last round comments --- src/mc-range-encoding.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/mc-range-encoding.c b/src/mc-range-encoding.c index c2408635b..c4ae7aa02 100644 --- a/src/mc-range-encoding.c +++ b/src/mc-range-encoding.c @@ -442,11 +442,13 @@ static mlib_int128 dec128_to_uint128(mc_dec128 dec) { return ret; } -#define SIGNED_INT_128_MAX_DECIMAL mc_dec128_from_string("170141183460469231731687303715884105727") -#define UNSIGNED_INT_128_MAX_DECIMAL mc_dec128_from_string("340282366920938463463374607431768211455") +// (2^127 - 1) = the maximum signed 128-bit integer value, as a decimal128 +#define INT_128_MAX_AS_DECIMAL mc_dec128_from_string("170141183460469231731687303715884105727") +// (2^128 - 1) = the max unsigned 128-bit integer value, as a decimal128 +#define UINT_128_MAX_AS_DECIMAL mc_dec128_from_string("340282366920938463463374607431768211455") static mlib_int128 dec128_to_int128(mc_dec128 dec) { - BSON_ASSERT(mc_dec128_less(dec, SIGNED_INT_128_MAX_DECIMAL)); + BSON_ASSERT(mc_dec128_less(dec, INT_128_MAX_AS_DECIMAL)); bool negative = false; @@ -530,24 +532,26 @@ bool mc_canUsePrecisionModeDecimal(mc_dec128 min, return false; } - if (mc_dec128_greater(mc_dec128_abs(scaled_max), SIGNED_INT_128_MAX_DECIMAL)) { + if (mc_dec128_greater(mc_dec128_abs(scaled_max), INT_128_MAX_AS_DECIMAL)) { CLIENT_ERR("Invalid upper bound for Decimal128 precision. Absolute scaled value must be less than " "or equal to %s. max: %s", - mc_dec128_to_string(SIGNED_INT_128_MAX_DECIMAL).str, + mc_dec128_to_string(INT_128_MAX_AS_DECIMAL).str, mc_dec128_to_string(max).str); return false; } - if (mc_dec128_greater(mc_dec128_abs(scaled_min), SIGNED_INT_128_MAX_DECIMAL)) { + if (mc_dec128_greater(mc_dec128_abs(scaled_min), INT_128_MAX_AS_DECIMAL)) { CLIENT_ERR("Invalid lower bound for Decimal128 precision. Absolute scaled value must be less than " "or equal to %s. min: %s", - mc_dec128_to_string(SIGNED_INT_128_MAX_DECIMAL).str, + mc_dec128_to_string(INT_128_MAX_AS_DECIMAL).str, mc_dec128_to_string(min).str); return false; } mc_dec128 t_1 = mc_dec128_sub(scaled_max, scaled_min); - mc_dec128 t_4 = mc_dec128_sub(UNSIGNED_INT_128_MAX_DECIMAL, t_1); + mc_dec128 t_4 = mc_dec128_sub(UINT_128_MAX_AS_DECIMAL, t_1); + + // t_5 = floor(log10(t_4)) - 1; mc_dec128 t_5 = mc_dec128_sub(mc_dec128_round_integral_ex(mc_dec128_log10(t_4), MC_DEC128_ROUND_TOWARD_ZERO, NULL), MC_DEC128(1));