/* * Copyright 2022-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // mc-range-mincover-generator.template.h is meant to be included in another // source file. // TODO: replace `CONCAT` with `BSON_CONCAT` after libbson dependency is // upgraded to 1.20.0 or higher. #ifndef CONCAT #define CONCAT_1(a, b) a##b #define CONCAT(a, b) CONCAT_1(a, b) #endif // TODO: replace `CONCAT3` with `BSON_CONCAT3` after libbson dependency is // upgraded to 1.20.0 or higher. #ifndef CONCAT3 #define CONCAT3(a, b, c) CONCAT(a, CONCAT(b, c)) #endif #if !(defined(UINT_T) && defined(UINT_C) && defined(UINT_FMT_S) && defined(DECORATE_NAME)) #ifdef __INTELLISENSE__ #define UINT_T uint32_t #define UINT_C UINT32_C #define UINT_FMT_S PRIu32 #define DECORATE_NAME(Name) Name##_u32 #else #error All of UINT_T, UINT_C, UINT_FMT_S, UINT_FMT_ARG, and DECORATE_NAME must be defined before #including this file #endif #endif #define BITS (sizeof(UINT_T) * CHAR_BIT) #define ZERO UINT_C(0) // Default for UINT_FMT_ARG #ifndef UINT_FMT_ARG #define UINT_FMT_ARG(X) X #endif // Default comparison #ifndef UINT_LESSTHAN #define UINT_LESSTHAN(A, B) ((A) < (B)) #endif #ifndef MC_UINT_MAX #define MC_UINT_MAX ~(UINT_C(0)) #endif // Default addition #ifndef UINT_ADD #define UINT_ADD(A, B) ((A) + (B)) #endif #ifndef UINT_SUB #define UINT_SUB(A, B) ((A) - (B)) #endif // Default lshift (also handles negatives as right-shift) #ifndef UINT_LSHIFT static inline UINT_T DECORATE_NAME(_mc_default_lshift)(UINT_T lhs, int off) { if (off < 0) { return lhs >> -off; } else { return lhs << off; } } #define UINT_LSHIFT DECORATE_NAME(_mc_default_lshift) #endif #ifndef UINT_BITOR #define UINT_BITOR(A, B) ((A) | (B)) #endif static inline int DECORATE_NAME(_mc_compare)(UINT_T lhs, UINT_T rhs) { if (UINT_LESSTHAN(lhs, rhs)) { return -1; } else if (UINT_LESSTHAN(rhs, lhs)) { return 1; } else { return 0; } } #define UINT_COMPARE DECORATE_NAME(_mc_compare) // MinCoverGenerator models the MinCoverGenerator type added in // SERVER-68600. typedef struct { UINT_T _rangeMin; UINT_T _rangeMax; size_t _sparsity; // _maxlen is the maximum bit length of edges in the mincover. size_t _maxlen; } DECORATE_NAME(MinCoverGenerator); static inline DECORATE_NAME(MinCoverGenerator) * DECORATE_NAME(MinCoverGenerator_new)(UINT_T rangeMin, UINT_T rangeMax, UINT_T max, size_t sparsity, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(status); if (UINT_COMPARE(rangeMin, rangeMax) > 0) { CLIENT_ERR("Range min (%" UINT_FMT_S ") must be less than or equal to range max (%" UINT_FMT_S ") for range search", UINT_FMT_ARG(rangeMin), UINT_FMT_ARG(rangeMax)); return NULL; } if (UINT_COMPARE(rangeMax, max) > 0) { CLIENT_ERR("Range max (%" UINT_FMT_S ") must be less than or equal to max (%" UINT_FMT_S ") for range search", UINT_FMT_ARG(rangeMax), UINT_FMT_ARG(max)); return NULL; } if (sparsity == 0) { CLIENT_ERR("Sparsity must be > 0"); return NULL; } DECORATE_NAME(MinCoverGenerator) *mcg = bson_malloc0(sizeof(DECORATE_NAME(MinCoverGenerator))); mcg->_rangeMin = rangeMin; mcg->_rangeMax = rangeMax; mcg->_maxlen = (size_t)BITS - DECORATE_NAME(mc_count_leading_zeros)(max); mcg->_sparsity = sparsity; return mcg; } static inline void DECORATE_NAME(MinCoverGenerator_destroy)(DECORATE_NAME(MinCoverGenerator) * mcg) { bson_free(mcg); } // applyMask applies a mask of 1 bits starting from the right. // Bits 0 to bit-1 are replaced with 1. Other bits are left as-is. static inline UINT_T DECORATE_NAME(applyMask)(UINT_T value, size_t maskedBits) { const UINT_T ones = MC_UINT_MAX; BSON_ASSERT(maskedBits <= (size_t)BITS); BSON_ASSERT(maskedBits >= 0); if (maskedBits == 0) { return value; } const size_t shift = ((size_t)BITS - maskedBits); const UINT_T mask = UINT_LSHIFT(ones, -(int)shift); return UINT_BITOR(value, mask); } static inline bool DECORATE_NAME(MinCoverGenerator_isLevelStored)(DECORATE_NAME(MinCoverGenerator) * mcg, size_t maskedBits) { BSON_ASSERT_PARAM(mcg); size_t level = mcg->_maxlen - maskedBits; return 0 == maskedBits || 0 == (level % mcg->_sparsity); } char * DECORATE_NAME(MinCoverGenerator_toString)(DECORATE_NAME(MinCoverGenerator) * mcg, UINT_T start, size_t maskedBits) { BSON_ASSERT_PARAM(mcg); BSON_ASSERT(maskedBits <= mcg->_maxlen); BSON_ASSERT(maskedBits <= (size_t)BITS); BSON_ASSERT(maskedBits >= 0); if (maskedBits == mcg->_maxlen) { return bson_strdup("root"); } UINT_T shifted = UINT_LSHIFT(start, -(int)maskedBits); mc_bitstring valueBin = DECORATE_NAME(mc_convert_to_bitstring)(shifted); char *ret = bson_strndup(valueBin.str + ((size_t)BITS - mcg->_maxlen + maskedBits), mcg->_maxlen + maskedBits); return ret; } static inline void DECORATE_NAME(MinCoverGenerator_minCoverRec)(DECORATE_NAME(MinCoverGenerator) * mcg, mc_array_t *c, UINT_T blockStart, size_t maskedBits) { BSON_ASSERT_PARAM(mcg); BSON_ASSERT_PARAM(c); const UINT_T blockEnd = DECORATE_NAME(applyMask)(blockStart, maskedBits); if (UINT_COMPARE(blockEnd, mcg->_rangeMin) < 0 || UINT_COMPARE(blockStart, mcg->_rangeMax) > 0) { return; } if (UINT_COMPARE(blockStart, mcg->_rangeMin) >= 0 && UINT_COMPARE(blockEnd, mcg->_rangeMax) <= 0 && DECORATE_NAME(MinCoverGenerator_isLevelStored)(mcg, maskedBits)) { char *edge = DECORATE_NAME(MinCoverGenerator_toString)(mcg, blockStart, maskedBits); _mc_array_append_val(c, edge); return; } BSON_ASSERT(maskedBits > 0); const size_t newBits = maskedBits - 1u; DECORATE_NAME(MinCoverGenerator_minCoverRec)(mcg, c, blockStart, newBits); DECORATE_NAME(MinCoverGenerator_minCoverRec) (mcg, c, UINT_BITOR(blockStart, UINT_LSHIFT(UINT_C(1), (int)newBits)), newBits); } static inline mc_mincover_t *DECORATE_NAME(MinCoverGenerator_minCover)(DECORATE_NAME(MinCoverGenerator) * mcg) { BSON_ASSERT_PARAM(mcg); mc_mincover_t *mc = mc_mincover_new(); DECORATE_NAME(MinCoverGenerator_minCoverRec) (mcg, &mc->mincover, ZERO, mcg->_maxlen); return mc; } // adjustBounds increments *lowerBound if includeLowerBound is false and // decrements *upperBound if includeUpperBound is false. // lowerBound, min, upperBound, and max are expected to come from the result // of mc_getTypeInfo. static bool DECORATE_NAME(adjustBounds)(UINT_T *lowerBound, bool includeLowerBound, UINT_T min, UINT_T *upperBound, bool includeUpperBound, UINT_T max, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(lowerBound); BSON_ASSERT_PARAM(upperBound); if (!includeLowerBound) { if (UINT_COMPARE(*lowerBound, max) >= 0) { CLIENT_ERR("Lower bound (%" UINT_FMT_S ") must be less than the range maximum (%" UINT_FMT_S ") if lower bound is excluded from range.", UINT_FMT_ARG(*lowerBound), UINT_FMT_ARG(max)); return false; } *lowerBound = UINT_ADD(*lowerBound, UINT_C(1)); } if (!includeUpperBound) { if (UINT_COMPARE(*upperBound, min) <= 0) { CLIENT_ERR("Upper bound (%" UINT_FMT_S ") must be greater than the range minimum (%" UINT_FMT_S ") if upper bound is excluded from range.", UINT_FMT_ARG(*upperBound), UINT_FMT_ARG(min)); return false; } *upperBound = UINT_SUB(*upperBound, UINT_C(1)); } return true; } #undef UINT_T #undef UINT_C #undef UINT_FMT_S #undef UINT_FMT_ARG #undef DECORATE_NAME #undef BITS #undef UINT_COMPARE #undef UINT_ADD #undef UINT_SUB #undef UINT_LSHIFT #undef UINT_BITOR #undef MC_UINT_MAX #undef ZERO #undef UINT_LESSTHAN