diff --git a/src/bson/bson-atomic.c b/src/bson/bson-atomic.c index b89aaf1..43915e0 100644 --- a/src/bson/bson-atomic.c +++ b/src/bson/bson-atomic.c @@ -25,7 +25,7 @@ int32_t bson_atomic_int_add (volatile int32_t *p, int32_t n) { - return n + bson_atomic_int32_fetch_add (p, n, bson_memory_order_seq_cst); + return n + bson_atomic_int32_fetch_add ((DECL_ATOMIC_INTEGRAL_INT32 *) p, n, bson_memory_order_seq_cst); } int64_t @@ -54,7 +54,7 @@ bson_memory_barrier (void) static int8_t gEmulAtomicLock = 0; static void -_lock_emul_atomic () +_lock_emul_atomic (void) { int i; if (bson_atomic_int8_compare_exchange_weak ( @@ -78,7 +78,7 @@ _lock_emul_atomic () } static void -_unlock_emul_atomic () +_unlock_emul_atomic (void) { int64_t rv = bson_atomic_int8_exchange ( &gEmulAtomicLock, 0, bson_memory_order_release); @@ -91,6 +91,9 @@ _bson_emul_atomic_int64_fetch_add (volatile int64_t *p, enum bson_memory_order _unused) { int64_t ret; + + BSON_UNUSED (_unused); + _lock_emul_atomic (); ret = *p; *p += n; @@ -104,6 +107,9 @@ _bson_emul_atomic_int64_exchange (volatile int64_t *p, enum bson_memory_order _unused) { int64_t ret; + + BSON_UNUSED (_unused); + _lock_emul_atomic (); ret = *p; *p = n; @@ -118,6 +124,9 @@ _bson_emul_atomic_int64_compare_exchange_strong (volatile int64_t *p, enum bson_memory_order _unused) { int64_t ret; + + BSON_UNUSED (_unused); + _lock_emul_atomic (); ret = *p; if (ret == expect_value) { @@ -145,6 +154,9 @@ _bson_emul_atomic_int32_fetch_add (volatile int32_t *p, enum bson_memory_order _unused) { int32_t ret; + + BSON_UNUSED (_unused); + _lock_emul_atomic (); ret = *p; *p += n; @@ -158,6 +170,9 @@ _bson_emul_atomic_int32_exchange (volatile int32_t *p, enum bson_memory_order _unused) { int32_t ret; + + BSON_UNUSED (_unused); + _lock_emul_atomic (); ret = *p; *p = n; @@ -172,6 +187,9 @@ _bson_emul_atomic_int32_compare_exchange_strong (volatile int32_t *p, enum bson_memory_order _unused) { int32_t ret; + + BSON_UNUSED (_unused); + _lock_emul_atomic (); ret = *p; if (ret == expect_value) { @@ -195,10 +213,13 @@ _bson_emul_atomic_int32_compare_exchange_weak (volatile int32_t *p, int _bson_emul_atomic_int_fetch_add (volatile int *p, - int n, - enum bson_memory_order _unused) + int n, + enum bson_memory_order _unused) { int ret; + + BSON_UNUSED (_unused); + _lock_emul_atomic (); ret = *p; *p += n; @@ -208,10 +229,13 @@ _bson_emul_atomic_int_fetch_add (volatile int *p, int _bson_emul_atomic_int_exchange (volatile int *p, - int n, - enum bson_memory_order _unused) + int n, + enum bson_memory_order _unused) { int ret; + + BSON_UNUSED (_unused); + _lock_emul_atomic (); ret = *p; *p = n; @@ -221,11 +245,14 @@ _bson_emul_atomic_int_exchange (volatile int *p, int _bson_emul_atomic_int_compare_exchange_strong (volatile int *p, - int expect_value, - int new_value, - enum bson_memory_order _unused) + int expect_value, + int new_value, + enum bson_memory_order _unused) { int ret; + + BSON_UNUSED (_unused); + _lock_emul_atomic (); ret = *p; if (ret == expect_value) { @@ -237,11 +264,27 @@ _bson_emul_atomic_int_compare_exchange_strong (volatile int *p, int _bson_emul_atomic_int_compare_exchange_weak (volatile int *p, - int expect_value, - int new_value, - enum bson_memory_order order) + int expect_value, + int new_value, + enum bson_memory_order order) { /* We're emulating. We can't do a weak version. */ return _bson_emul_atomic_int_compare_exchange_strong ( p, expect_value, new_value, order); } + +void * +_bson_emul_atomic_ptr_exchange (void *volatile *p, + void *n, + enum bson_memory_order _unused) +{ + void *ret; + + BSON_UNUSED (_unused); + + _lock_emul_atomic (); + ret = *p; + *p = n; + _unlock_emul_atomic (); + return ret; +} diff --git a/src/bson/bson-atomic.h b/src/bson/bson-atomic.h index 190283b..51af1ac 100644 --- a/src/bson/bson-atomic.h +++ b/src/bson/bson-atomic.h @@ -47,8 +47,7 @@ enum bson_memory_order { #define MSVC_MEMORDER_SUFFIX(X) #endif -#if defined(USE_LEGACY_GCC_ATOMICS) || \ - (!defined(__clang__) && __GNUC__ == 4) +#if defined(USE_LEGACY_GCC_ATOMICS) || (!defined(__clang__) && __GNUC__ == 4) || defined(__xlC__) #define BSON_USE_LEGACY_GCC_ATOMICS #else #undef BSON_USE_LEGACY_GCC_ATOMICS @@ -62,6 +61,7 @@ enum bson_memory_order { #ifdef BSON_USE_LEGACY_GCC_ATOMICS #undef BSON_IF_GNU_LIKE #define BSON_IF_GNU_LIKE(...) +#define BSON_IF_MSVC(...) #define BSON_IF_GNU_LEGACY_ATOMICS(...) __VA_ARGS__ #else #define BSON_IF_GNU_LEGACY_ATOMICS(...) @@ -74,7 +74,14 @@ enum bson_memory_order { #define BSON_EMULATE_INT #endif -#define DEF_ATOMIC_OP(MSVC_Intrinsic, GNU_Intrinsic, GNU_Legacy_Intrinsic, Order, ...) \ +/* CDRIVER-4264 Contrary to documentation, VS 2013 targeting x86 does not + * correctly/consistently provide _InterlockedPointerExchange. */ +#if defined(_MSC_VER) && _MSC_VER < 1900 && defined(_M_IX86) +#define BSON_EMULATE_PTR +#endif + +#define DEF_ATOMIC_OP( \ + MSVC_Intrinsic, GNU_Intrinsic, GNU_Legacy_Intrinsic, Order, ...) \ do { \ switch (Order) { \ case bson_memory_order_acq_rel: \ @@ -127,47 +134,43 @@ enum bson_memory_order { } while (0) -#define DEF_ATOMIC_CMPEXCH_STRONG( \ - VCSuffix1, VCSuffix2, GNU_MemOrder, Ptr, ExpectActualVar, NewValue) \ - do { \ - BSON_IF_MSVC (ExpectActualVar = BSON_CONCAT3 ( \ - _InterlockedCompareExchange, VCSuffix1, VCSuffix2) ( \ - Ptr, NewValue, ExpectActualVar);) \ - BSON_IF_GNU_LIKE ( \ - (void) __atomic_compare_exchange_n (Ptr, \ - &ExpectActualVar, \ - NewValue, \ - false, /* Not weak */ \ - GNU_MemOrder, \ - GNU_MemOrder);) \ - BSON_IF_GNU_LEGACY_ATOMICS ( \ - __typeof__ (ExpectActualVar) _val; \ - _val = __sync_val_compare_and_swap (Ptr, \ - ExpectActualVar, \ - NewValue); \ - ExpectActualVar = _val;) \ +#define DEF_ATOMIC_CMPEXCH_STRONG( \ + VCSuffix1, VCSuffix2, GNU_MemOrder, Ptr, ExpectActualVar, NewValue) \ + do { \ + BSON_IF_MSVC (ExpectActualVar = BSON_CONCAT3 ( \ + _InterlockedCompareExchange, VCSuffix1, VCSuffix2) ( \ + Ptr, NewValue, ExpectActualVar);) \ + BSON_IF_GNU_LIKE ( \ + (void) __atomic_compare_exchange_n (Ptr, \ + &ExpectActualVar, \ + NewValue, \ + false, /* Not weak */ \ + GNU_MemOrder, \ + GNU_MemOrder);) \ + BSON_IF_GNU_LEGACY_ATOMICS ( \ + __typeof__ (ExpectActualVar) _val; \ + _val = __sync_val_compare_and_swap (Ptr, ExpectActualVar, NewValue); \ + ExpectActualVar = _val;) \ } while (0) -#define DEF_ATOMIC_CMPEXCH_WEAK( \ - VCSuffix1, VCSuffix2, GNU_MemOrder, Ptr, ExpectActualVar, NewValue) \ - do { \ - BSON_IF_MSVC (ExpectActualVar = BSON_CONCAT3 ( \ - _InterlockedCompareExchange, VCSuffix1, VCSuffix2) ( \ - Ptr, NewValue, ExpectActualVar);) \ - BSON_IF_GNU_LIKE ( \ - (void) __atomic_compare_exchange_n (Ptr, \ - &ExpectActualVar, \ - NewValue, \ - true, /* Yes weak */ \ - GNU_MemOrder, \ - GNU_MemOrder);) \ - BSON_IF_GNU_LEGACY_ATOMICS ( \ - __typeof__ (ExpectActualVar) _val; \ - _val = __sync_val_compare_and_swap (Ptr, \ - ExpectActualVar, \ - NewValue); \ - ExpectActualVar = _val;) \ +#define DEF_ATOMIC_CMPEXCH_WEAK( \ + VCSuffix1, VCSuffix2, GNU_MemOrder, Ptr, ExpectActualVar, NewValue) \ + do { \ + BSON_IF_MSVC (ExpectActualVar = BSON_CONCAT3 ( \ + _InterlockedCompareExchange, VCSuffix1, VCSuffix2) ( \ + Ptr, NewValue, ExpectActualVar);) \ + BSON_IF_GNU_LIKE ( \ + (void) __atomic_compare_exchange_n (Ptr, \ + &ExpectActualVar, \ + NewValue, \ + true, /* Yes weak */ \ + GNU_MemOrder, \ + GNU_MemOrder);) \ + BSON_IF_GNU_LEGACY_ATOMICS ( \ + __typeof__ (ExpectActualVar) _val; \ + _val = __sync_val_compare_and_swap (Ptr, ExpectActualVar, NewValue); \ + ExpectActualVar = _val;) \ } while (0) @@ -217,7 +220,10 @@ enum bson_memory_order { default: \ BSON_UNREACHABLE ("Invalid bson_memory_order value"); \ }) \ - BSON_IF_GNU_LEGACY_ATOMICS ({ __sync_synchronize (); return *a; }) \ + BSON_IF_GNU_LEGACY_ATOMICS ({ \ + __sync_synchronize (); \ + return *a; \ + }) \ } \ \ static BSON_INLINE Type bson_atomic_##NamePart##_exchange ( \ @@ -342,28 +348,42 @@ enum bson_memory_order { #define DECL_ATOMIC_STDINT(Name, VCSuffix) \ DECL_ATOMIC_INTEGRAL (Name, Name##_t, VCSuffix) -#if defined(_MSC_VER) || defined (BSON_USE_LEGACY_GCC_ATOMICS) -/* MSVC expects precise types for their atomic intrinsics. */ -DECL_ATOMIC_INTEGRAL (int8, char, 8); +#if defined(_MSC_VER) || defined(BSON_USE_LEGACY_GCC_ATOMICS) +/* MSVC and GCC require built-in types (not typedefs) for their atomic + * intrinsics. */ +#if defined(_MSC_VER) +#define DECL_ATOMIC_INTEGRAL_INT8 char +#define DECL_ATOMIC_INTEGRAL_INT32 long +#define DECL_ATOMIC_INTEGRAL_INT long +#else +#define DECL_ATOMIC_INTEGRAL_INT8 signed char +#define DECL_ATOMIC_INTEGRAL_INT32 int +#define DECL_ATOMIC_INTEGRAL_INT int +#endif +DECL_ATOMIC_INTEGRAL (int8, DECL_ATOMIC_INTEGRAL_INT8, 8) DECL_ATOMIC_INTEGRAL (int16, short, 16) -#if !defined (BSON_EMULATE_INT32) -DECL_ATOMIC_INTEGRAL (int32, long, ) +#if !defined(BSON_EMULATE_INT32) +DECL_ATOMIC_INTEGRAL (int32, DECL_ATOMIC_INTEGRAL_INT32, ) #endif -#if !defined (BSON_EMULATE_INT) -DECL_ATOMIC_INTEGRAL (int, long, ) +#if !defined(BSON_EMULATE_INT) +DECL_ATOMIC_INTEGRAL (int, DECL_ATOMIC_INTEGRAL_INT, ) #endif #else /* Other compilers that we support provide generic intrinsics */ DECL_ATOMIC_STDINT (int8, 8) DECL_ATOMIC_STDINT (int16, 16) -#if !defined (BSON_EMULATE_INT32) +#if !defined(BSON_EMULATE_INT32) DECL_ATOMIC_STDINT (int32, ) #endif -#if !defined (BSON_EMULATE_INT) +#if !defined(BSON_EMULATE_INT) DECL_ATOMIC_INTEGRAL (int, int, ) #endif #endif +#ifndef DECL_ATOMIC_INTEGRAL_INT32 +#define DECL_ATOMIC_INTEGRAL_INT32 int32_t +#endif + BSON_EXPORT (int64_t) _bson_emul_atomic_int64_fetch_add (int64_t volatile *val, int64_t v, @@ -424,6 +444,11 @@ _bson_emul_atomic_int_compare_exchange_weak (int volatile *val, int new_value, enum bson_memory_order); +BSON_EXPORT (void *) +_bson_emul_atomic_ptr_exchange (void *volatile *val, + void *v, + enum bson_memory_order); + BSON_EXPORT (void) bson_thrd_yield (void); @@ -599,8 +624,10 @@ bson_atomic_ptr_exchange (void *volatile *ptr, void *new_value, enum bson_memory_order ord) { +#if defined(BSON_EMULATE_PTR) + return _bson_emul_atomic_ptr_exchange (ptr, new_value, ord); +#elif defined(BSON_USE_LEGACY_GCC_ATOMICS) /* The older __sync_val_compare_and_swap also takes oldval */ -#if defined(BSON_USE_LEGACY_GCC_ATOMICS) DEF_ATOMIC_OP (_InterlockedExchangePointer, , __sync_val_compare_and_swap, @@ -609,12 +636,8 @@ bson_atomic_ptr_exchange (void *volatile *ptr, *ptr, new_value); #else - DEF_ATOMIC_OP (_InterlockedExchangePointer, - __atomic_exchange_n, - , - ord, - ptr, - new_value); + DEF_ATOMIC_OP ( + _InterlockedExchangePointer, __atomic_exchange_n, , ord, ptr, new_value); #endif } @@ -722,7 +745,7 @@ bson_atomic_ptr_fetch (void *volatile const *ptr, enum bson_memory_order ord) * @brief Generate a full-fence memory barrier at the call site. */ static BSON_INLINE void -bson_atomic_thread_fence () +bson_atomic_thread_fence (void) { BSON_IF_MSVC (MemoryBarrier ();) BSON_IF_GNU_LIKE (__sync_synchronize ();) @@ -745,6 +768,8 @@ BSON_EXPORT (int32_t) bson_atomic_int_add (volatile int32_t *p, int32_t n); BSON_GNUC_DEPRECATED_FOR ("bson_atomic_int64_fetch_add") BSON_EXPORT (int64_t) bson_atomic_int64_add (volatile int64_t *p, int64_t n); + +#undef BSON_EMULATE_PTR #undef BSON_EMULATE_INT32 #undef BSON_EMULATE_INT diff --git a/src/bson/bson-clock.c b/src/bson/bson-clock.c index 95287dc..921bbc2 100644 --- a/src/bson/bson-clock.c +++ b/src/bson/bson-clock.c @@ -126,18 +126,8 @@ bson_get_monotonic_time (void) clock_gettime (CLOCK_MONOTONIC, &ts); return (((int64_t) ts.tv_sec * 1000000) + (ts.tv_nsec / 1000)); #elif defined(__APPLE__) - static mach_timebase_info_data_t info = {0}; - static double ratio = 0.0; - - if (!info.denom) { - /* the value from mach_absolute_time () * info.numer / info.denom - * is in nano seconds. So we have to divid by 1000.0 to get micro - * seconds*/ - mach_timebase_info (&info); - ratio = (double) info.numer / (double) info.denom / 1000.0; - } - - return mach_absolute_time () * ratio; + const uint64_t nsec = clock_gettime_nsec_np (CLOCK_UPTIME_RAW); + return (int64_t) (nsec / 1000u); #elif defined(_WIN32) /* Despite it's name, this is in milliseconds! */ int64_t ticks = GetTickCount64 (); diff --git a/src/bson/bson-cmp.h b/src/bson/bson-cmp.h new file mode 100644 index 0000000..98da848 --- /dev/null +++ b/src/bson/bson-cmp.h @@ -0,0 +1,197 @@ +/* + * Copyright 2022 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. + */ + +#include "bson-prelude.h" + + +#ifndef BSON_CMP_H +#define BSON_CMP_H + + +#include "bson-compat.h" /* ssize_t */ +#include "bson-macros.h" /* BSON_CONCAT */ + +#include +#include +#include + + +BSON_BEGIN_DECLS + + +/* Based on the "Safe Integral Comparisons" proposal merged in C++20: + * http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p0586r2.html + * + * Due to lack of type deduction in C, relational comparison functions (e.g. + * `cmp_less`) are defined in sets of four "functions" according to the + * signedness of each value argument, e.g.: + * - bson_cmp_less_ss (signed-value, signed-value) + * - bson_cmp_less_uu (unsigned-value, unsigned-value) + * - bson_cmp_less_su (signed-value, unsigned-value) + * - bson_cmp_less_us (unsigned-value, signed-value) + * + * Similarly, the `in_range` function is defined as a set of two "functions" + * according to the signedness of the value argument: + * - bson_in_range_signed (Type, signed-value) + * - bson_in_range_unsigned (Type, unsigned-value) + * + * The user must take care to use the correct signedness for the provided + * argument(s). Enabling compiler warnings for implicit sign conversions is + * recommended. + */ + + +#define BSON_CMP_SET(op, ss, uu, su, us) \ + static BSON_INLINE bool BSON_CONCAT3 (bson_cmp_, op, _ss) (int64_t t, \ + int64_t u) \ + { \ + return (ss); \ + } \ + \ + static BSON_INLINE bool BSON_CONCAT3 (bson_cmp_, op, _uu) (uint64_t t, \ + uint64_t u) \ + { \ + return (uu); \ + } \ + \ + static BSON_INLINE bool BSON_CONCAT3 (bson_cmp_, op, _su) (int64_t t, \ + uint64_t u) \ + { \ + return (su); \ + } \ + \ + static BSON_INLINE bool BSON_CONCAT3 (bson_cmp_, op, _us) (uint64_t t, \ + int64_t u) \ + { \ + return (us); \ + } + +BSON_CMP_SET (equal, + t == u, + t == u, + t < 0 ? false : (uint64_t) (t) == u, + u < 0 ? false : t == (uint64_t) (u)) + +BSON_CMP_SET (not_equal, + !bson_cmp_equal_ss (t, u), + !bson_cmp_equal_uu (t, u), + !bson_cmp_equal_su (t, u), + !bson_cmp_equal_us (t, u)) + +BSON_CMP_SET (less, + t < u, + t < u, + t < 0 ? true : (uint64_t) (t) < u, + u < 0 ? false : t < (uint64_t) (u)) + +BSON_CMP_SET (greater, + bson_cmp_less_ss (u, t), + bson_cmp_less_uu (u, t), + bson_cmp_less_us (u, t), + bson_cmp_less_su (u, t)) + +BSON_CMP_SET (less_equal, + !bson_cmp_greater_ss (t, u), + !bson_cmp_greater_uu (t, u), + !bson_cmp_greater_su (t, u), + !bson_cmp_greater_us (t, u)) + +BSON_CMP_SET (greater_equal, + !bson_cmp_less_ss (t, u), + !bson_cmp_less_uu (t, u), + !bson_cmp_less_su (t, u), + !bson_cmp_less_us (t, u)) + +#undef BSON_CMP_SET + + +/* Return true if the given value is within the range of the corresponding + * signed type. The suffix must match the signedness of the given value. */ +#define BSON_IN_RANGE_SET_SIGNED(Type, min, max) \ + static BSON_INLINE bool BSON_CONCAT3 (bson_in_range, _##Type, _signed) ( \ + int64_t value) \ + { \ + return bson_cmp_greater_equal_ss (value, min) && \ + bson_cmp_less_equal_ss (value, max); \ + } \ + \ + static BSON_INLINE bool BSON_CONCAT3 (bson_in_range, _##Type, _unsigned) ( \ + uint64_t value) \ + { \ + return bson_cmp_greater_equal_us (value, min) && \ + bson_cmp_less_equal_us (value, max); \ + } + +/* Return true if the given value is within the range of the corresponding + * unsigned type. The suffix must match the signedness of the given value. */ +#define BSON_IN_RANGE_SET_UNSIGNED(Type, max) \ + static BSON_INLINE bool BSON_CONCAT3 (bson_in_range, _##Type, _signed) ( \ + int64_t value) \ + { \ + return bson_cmp_greater_equal_su (value, 0u) && \ + bson_cmp_less_equal_su (value, max); \ + } \ + \ + static BSON_INLINE bool BSON_CONCAT3 (bson_in_range, _##Type, _unsigned) ( \ + uint64_t value) \ + { \ + return bson_cmp_less_equal_uu (value, max); \ + } + +BSON_IN_RANGE_SET_SIGNED (signed_char, SCHAR_MIN, SCHAR_MAX) +BSON_IN_RANGE_SET_SIGNED (short, SHRT_MIN, SHRT_MAX) +BSON_IN_RANGE_SET_SIGNED (int, INT_MIN, INT_MAX) +BSON_IN_RANGE_SET_SIGNED (long, LONG_MIN, LONG_MAX) +BSON_IN_RANGE_SET_SIGNED (long_long, LLONG_MIN, LLONG_MAX) + +BSON_IN_RANGE_SET_UNSIGNED (unsigned_char, UCHAR_MAX) +BSON_IN_RANGE_SET_UNSIGNED (unsigned_short, USHRT_MAX) +BSON_IN_RANGE_SET_UNSIGNED (unsigned_int, UINT_MAX) +BSON_IN_RANGE_SET_UNSIGNED (unsigned_long, ULONG_MAX) +BSON_IN_RANGE_SET_UNSIGNED (unsigned_long_long, ULLONG_MAX) + +BSON_IN_RANGE_SET_SIGNED (int8_t, INT8_MIN, INT8_MAX) +BSON_IN_RANGE_SET_SIGNED (int16_t, INT16_MIN, INT16_MAX) +BSON_IN_RANGE_SET_SIGNED (int32_t, INT32_MIN, INT32_MAX) +BSON_IN_RANGE_SET_SIGNED (int64_t, INT64_MIN, INT64_MAX) + +BSON_IN_RANGE_SET_UNSIGNED (uint8_t, UINT8_MAX) +BSON_IN_RANGE_SET_UNSIGNED (uint16_t, UINT16_MAX) +BSON_IN_RANGE_SET_UNSIGNED (uint32_t, UINT32_MAX) +BSON_IN_RANGE_SET_UNSIGNED (uint64_t, UINT64_MAX) + +BSON_IN_RANGE_SET_SIGNED (ssize_t, SSIZE_MIN, SSIZE_MAX) +BSON_IN_RANGE_SET_UNSIGNED (size_t, SIZE_MAX) + +#undef BSON_IN_RANGE_SET_SIGNED +#undef BSON_IN_RANGE_SET_UNSIGNED + + +/* Return true if the value with *signed* type is in the representable range of + * Type and false otherwise. */ +#define bson_in_range_signed(Type, value) \ + BSON_CONCAT3 (bson_in_range, _##Type, _signed) (value) + +/* Return true if the value with *unsigned* type is in the representable range + * of Type and false otherwise. */ +#define bson_in_range_unsigned(Type, value) \ + BSON_CONCAT3 (bson_in_range, _##Type, _unsigned) (value) + + +BSON_END_DECLS + + +#endif /* BSON_CMP_H */ diff --git a/src/bson/bson-compat.h b/src/bson/bson-compat.h index d6556a2..3358f58 100644 --- a/src/bson/bson-compat.h +++ b/src/bson/bson-compat.h @@ -130,6 +130,29 @@ typedef SSIZE_T ssize_t; #endif #endif +/* Derive the maximum representable value of signed integer type T using the + * formula 2^(N - 1) - 1 where N is the number of bits in type T. This assumes + * T is represented using two's complement. */ +#define BSON_NUMERIC_LIMITS_MAX_SIGNED(T) \ + ((T) ((((size_t) 0x01u) << (sizeof (T) * (size_t) CHAR_BIT - 1u)) - 1u)) + +/* Derive the minimum representable value of signed integer type T as one less + * than the negation of its maximum representable value. This assumes T is + * represented using two's complement. */ +#define BSON_NUMERIC_LIMITS_MIN_SIGNED(T, max) ((T) ((-(max)) - 1)) + +/* Derive the maximum representable value of unsigned integer type T by flipping + * all its bits to 1. */ +#define BSON_NUMERIC_LIMITS_MAX_UNSIGNED(T) ((T) (~((T) 0))) + +#ifndef SSIZE_MAX +#define SSIZE_MAX BSON_NUMERIC_LIMITS_MAX_SIGNED (ssize_t) +#endif + +#ifndef SSIZE_MIN +#define SSIZE_MIN BSON_NUMERIC_LIMITS_MIN_SIGNED (ssize_t, SSIZE_MAX) +#endif + #if defined(__MINGW32__) && !defined(INIT_ONCE_STATIC_INIT) #define INIT_ONCE_STATIC_INIT RTL_RUN_ONCE_INIT typedef RTL_RUN_ONCE INIT_ONCE; @@ -152,9 +175,9 @@ typedef signed char bool; #define bson_sync_synchronize() __sync_synchronize () #elif defined(__i386__) || defined(__i486__) || defined(__i586__) || \ defined(__i686__) || defined(__x86_64__) -#define bson_sync_synchronize() asm volatile("mfence" ::: "memory") +#define bson_sync_synchronize() asm volatile ("mfence" ::: "memory") #else -#define bson_sync_synchronize() asm volatile("sync" ::: "memory") +#define bson_sync_synchronize() asm volatile ("sync" ::: "memory") #endif #elif defined(_MSC_VER) #define bson_sync_synchronize() MemoryBarrier () diff --git a/src/bson/bson-config.h b/src/bson/bson-config.h index 9b3bd49..baf7e5b 100644 --- a/src/bson/bson-config.h +++ b/src/bson/bson-config.h @@ -42,7 +42,7 @@ /* * Define to 1 if you have stdbool.h */ -#define BSON_HAVE_STDBOOL_H 0 +#define BSON_HAVE_STDBOOL_H 1 #if BSON_HAVE_STDBOOL_H != 1 # undef BSON_HAVE_STDBOOL_H #endif diff --git a/src/bson/bson-context-private.h b/src/bson/bson-context-private.h index 5b0d444..c88c87c 100644 --- a/src/bson/bson-context-private.h +++ b/src/bson/bson-context-private.h @@ -28,24 +28,55 @@ BSON_BEGIN_DECLS +enum { + BSON_OID_RANDOMESS_OFFSET = 4, + BSON_OID_RANDOMNESS_SIZE = 5, + BSON_OID_SEQ32_OFFSET = 9, + BSON_OID_SEQ32_SIZE = 3, + BSON_OID_SEQ64_OFFSET = 4, + BSON_OID_SEQ64_SIZE = 8 +}; + struct _bson_context_t { /* flags are defined in bson_context_flags_t */ int flags; - int32_t seq32; - int64_t seq64; - uint8_t rand[5]; - uint16_t pid; - - void (*oid_set_seq32) (bson_context_t *context, bson_oid_t *oid); - void (*oid_set_seq64) (bson_context_t *context, bson_oid_t *oid); - - /* this function pointer allows us to mock gethostname for testing. */ - void (*gethostname) (char *out); + uint32_t seq32; + uint64_t seq64; + uint8_t randomness[BSON_OID_RANDOMNESS_SIZE]; + uint64_t pid; }; +/** + * @brief Insert the context's randomness data into the given OID + * + * @param context A context for some random data + * @param oid The OID to update. + */ void _bson_context_set_oid_rand (bson_context_t *context, bson_oid_t *oid); +/** + * @brief Insert the context's sequence counter into the given OID. Increments + * the context's sequence counter. + * + * @param context The context with the counter to get+update + * @param oid The OID to modify + */ +void +_bson_context_set_oid_seq32 (bson_context_t *context, bson_oid_t *oid); + +/** + * @brief Write a 64-bit counter from the given context into the OID. Increments + * the context's sequence counter. + * + * @param context The context with the counter to get+update + * @param oid The OID to modify + * + * @note Only used by the deprecated @ref bson_oid_init_sequence + */ +void +_bson_context_set_oid_seq64 (bson_context_t *context, bson_oid_t *oid); + BSON_END_DECLS diff --git a/src/bson/bson-context.c b/src/bson/bson-context.c index a8f5e12..f64edd4 100644 --- a/src/bson/bson-context.c +++ b/src/bson/bson-context.c @@ -29,10 +29,6 @@ #include "bson-memory.h" #include "common-thread-private.h" -#ifdef BSON_HAVE_SYSCALL_TID -#include -#endif - #ifndef HOST_NAME_MAX #define HOST_NAME_MAX 256 @@ -44,201 +40,47 @@ */ static bson_context_t gContextDefault; -static BSON_INLINE uint16_t +static BSON_INLINE uint64_t _bson_getpid (void) { - uint16_t pid; + uint64_t pid; #ifdef BSON_OS_WIN32 DWORD real_pid; real_pid = GetCurrentProcessId (); pid = (real_pid & 0xFFFF) ^ ((real_pid >> 16) & 0xFFFF); #else - pid = getpid (); + pid = (uint64_t) getpid (); #endif return pid; } -/* - *-------------------------------------------------------------------------- - * - * _bson_context_set_oid_seq32 -- - * - * 32-bit sequence generator, non-thread-safe version. - * - * Returns: - * None. - * - * Side effects: - * @oid is modified. - * - *-------------------------------------------------------------------------- - */ -static void +void _bson_context_set_oid_seq32 (bson_context_t *context, /* IN */ bson_oid_t *oid) /* OUT */ { - uint32_t seq = context->seq32++; - - seq = BSON_UINT32_TO_BE (seq); - memcpy (&oid->bytes[9], ((uint8_t *) &seq) + 1, 3); -} - - -/* - *-------------------------------------------------------------------------- - * - * _bson_context_set_oid_seq32_threadsafe -- - * - * Thread-safe version of 32-bit sequence generator. - * - * Returns: - * None. - * - * Side effects: - * @oid is modified. - * - *-------------------------------------------------------------------------- - */ - -static void -_bson_context_set_oid_seq32_threadsafe (bson_context_t *context, /* IN */ - bson_oid_t *oid) /* OUT */ -{ - int32_t seq = 1 + bson_atomic_int32_fetch_add ( - &context->seq32, 1, bson_memory_order_seq_cst); + uint32_t seq = (uint32_t) bson_atomic_int32_fetch_add ( + (DECL_ATOMIC_INTEGRAL_INT32 *) &context->seq32, 1, bson_memory_order_seq_cst); seq = BSON_UINT32_TO_BE (seq); - memcpy (&oid->bytes[9], ((uint8_t *) &seq) + 1, 3); + memcpy (&oid->bytes[BSON_OID_SEQ32_OFFSET], + ((uint8_t *) &seq) + 1, + BSON_OID_SEQ32_SIZE); } -/* - *-------------------------------------------------------------------------- - * - * _bson_context_set_oid_seq64 -- - * - * 64-bit oid sequence generator, non-thread-safe version. - * - * Returns: - * None. - * - * Side effects: - * @oid is modified. - * - *-------------------------------------------------------------------------- - */ - -static void +void _bson_context_set_oid_seq64 (bson_context_t *context, /* IN */ bson_oid_t *oid) /* OUT */ { - uint64_t seq; - - BSON_ASSERT (context); - BSON_ASSERT (oid); - - seq = BSON_UINT64_TO_BE (context->seq64++); - memcpy (&oid->bytes[4], &seq, sizeof (seq)); -} - - -/* - *-------------------------------------------------------------------------- - * - * _bson_context_set_oid_seq64_threadsafe -- - * - * Thread-safe 64-bit sequence generator. - * - * Returns: - * None. - * - * Side effects: - * @oid is modified. - * - *-------------------------------------------------------------------------- - */ - -static void -_bson_context_set_oid_seq64_threadsafe (bson_context_t *context, /* IN */ - bson_oid_t *oid) /* OUT */ -{ - int64_t seq = 1 + bson_atomic_int64_fetch_add ( - &context->seq64, 1, bson_memory_order_seq_cst); + uint64_t seq = (uint64_t) bson_atomic_int64_fetch_add ( + (int64_t *) &context->seq64, 1, bson_memory_order_seq_cst); seq = BSON_UINT64_TO_BE (seq); - memcpy (&oid->bytes[4], &seq, sizeof (seq)); -} - - -static void -_bson_context_init_random (bson_context_t *context, bool init_sequence); - -/* - *-------------------------------------------------------------------------- - * - * _bson_context_set_oid_rand -- - * - * Sets the process specific five byte random sequence in an oid. - * - * Returns: - * None. - * - * Side effects: - * @oid is modified. - * - *-------------------------------------------------------------------------- - */ -void -_bson_context_set_oid_rand (bson_context_t *context, bson_oid_t *oid) -{ - BSON_ASSERT (context); - BSON_ASSERT (oid); - - if (context->flags & BSON_CONTEXT_DISABLE_PID_CACHE) { - uint16_t pid = _bson_getpid (); - - if (pid != context->pid) { - context->pid = pid; - /* randomize the random bytes, not the sequence. */ - _bson_context_init_random (context, false); - } - } - memcpy (&oid->bytes[4], &context->rand, sizeof (context->rand)); -} - -/* - *-------------------------------------------------------------------------- - * - * _get_rand -- - * - * Gets a random four byte integer. Callers that will use the "rand" - * function must call "srand" prior. - * - * Returns: - * A random int32_t. - * - *-------------------------------------------------------------------------- - */ -static int32_t -_get_rand (unsigned int *pseed) -{ - int32_t result = 0; -#ifndef BSON_HAVE_RAND_R - /* ms's runtime is multithreaded by default, so no rand_r */ - /* no rand_r on android either */ - result = rand (); -#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__) || \ - defined(__OpenBSD__) || defined(__APPLE__) - arc4random_buf (&result, sizeof (result)); -#else - result = rand_r (pseed); -#endif - return result; + memcpy (&oid->bytes[BSON_OID_SEQ64_OFFSET], &seq, BSON_OID_SEQ64_SIZE); } - /* * -------------------------------------------------------------------------- * @@ -250,7 +92,7 @@ _get_rand (unsigned int *pseed) * -------------------------------------------------------------------------- */ static void -_bson_context_get_hostname (char *out) +_bson_context_get_hostname (char out[HOST_NAME_MAX]) { if (gethostname (out, HOST_NAME_MAX) != 0) { if (errno == ENAMETOOLONG) { @@ -265,111 +107,242 @@ _bson_context_get_hostname (char *out) } -static void -_bson_context_init_random (bson_context_t *context, bool init_sequence) +/*** ======================================== + * The below SipHash implementation is based on the original public-domain + * reference implementation from Jean-Philippe Aumasson and DJB + * (https://github.com/veorq/SipHash). + */ + +/* in-place rotate a 64bit number */ +void +_bson_rotl_u64 (uint64_t *p, int nbits) { - int64_t rand_bytes; - struct timeval tv; - unsigned int seed = 0; - char hostname[HOST_NAME_MAX]; - char *ptr; - int hostname_chars_left; - - /* - * The seed consists of the following xor'd together: - * - current time in seconds - * - current time in milliseconds - * - current pid - * - current hostname - */ - bson_gettimeofday (&tv); - seed ^= (unsigned int) tv.tv_sec; - seed ^= (unsigned int) tv.tv_usec; - seed ^= (unsigned int) context->pid; - - context->gethostname (hostname); - hostname_chars_left = strlen (hostname); - ptr = hostname; - while (hostname_chars_left) { - uint32_t hostname_chunk = 0; - uint32_t to_copy = hostname_chars_left > 4 ? 4 : hostname_chars_left; - - memcpy (&hostname_chunk, ptr, to_copy); - seed ^= (unsigned int) hostname_chunk; - hostname_chars_left -= to_copy; - ptr += to_copy; - } + *p = (*p << nbits) | (*p >> (64 - nbits)); +} -#ifndef BSON_HAVE_RAND_R - srand (seed); -#endif +/* Write the little-endian representation of 'val' into 'out' */ +void +_u64_into_u8x8_le (uint8_t out[8], uint64_t val) +{ + val = BSON_UINT64_TO_LE (val); + memcpy (out, &val, sizeof val); +} + +/* Read a little-endian representation of a 64bit number from 'in' */ +uint64_t +_u8x8_le_to_u64 (const uint8_t in[8]) +{ + uint64_t r; + memcpy (&r, in, sizeof r); + return BSON_UINT64_FROM_LE (r); +} + +/* Perform one SipHash round */ +void +_sip_round (uint64_t *v0, uint64_t *v1, uint64_t *v2, uint64_t *v3) +{ + *v0 += *v1; + _bson_rotl_u64 (v1, 13); + *v1 ^= *v0; + _bson_rotl_u64 (v0, 32); + *v2 += *v3; + _bson_rotl_u64 (v3, 16); + *v3 ^= *v2; + *v0 += *v3; + _bson_rotl_u64 (v3, 21); + *v3 ^= *v0; + *v2 += *v1; + _bson_rotl_u64 (v1, 17); + *v1 ^= *v2; + _bson_rotl_u64 (v2, 32); +} + +void +_siphash (const void *in, + const size_t inlen, + const uint64_t key[2], + uint64_t digest[2]) +{ + const unsigned char *ni = (const unsigned char *) in; + const unsigned char *kk = (const unsigned char *) key; + uint8_t digest_buf[16] = {0}; + + const int C_ROUNDS = 2; + const int D_ROUNDS = 4; + + uint64_t v0 = UINT64_C (0x736f6d6570736575); + uint64_t v1 = UINT64_C (0x646f72616e646f6d); + uint64_t v2 = UINT64_C (0x6c7967656e657261); + uint64_t v3 = UINT64_C (0x7465646279746573); + uint64_t k0 = _u8x8_le_to_u64 (kk); + uint64_t k1 = _u8x8_le_to_u64 (kk + 8); + uint64_t m; + int i; + const unsigned char *end = ni + inlen - (inlen % sizeof (uint64_t)); + const int left = inlen & 7; + uint64_t b = ((uint64_t) inlen) << 56; + v3 ^= k1; + v2 ^= k0; + v1 ^= k1; + v0 ^= k0; + + v1 ^= 0xee; + + for (; ni != end; ni += 8) { + m = _u8x8_le_to_u64 (ni); + v3 ^= m; + + for (i = 0; i < C_ROUNDS; ++i) + _sip_round (&v0, &v1, &v2, &v3); + + v0 ^= m; + } - /* Generate a seed for the random starting position of our increment - * bytes and the five byte random number. */ - if (init_sequence) { - /* We mask off the last nibble so that the last digit of the OID will - * start at zero. Just to be nice. */ - context->seq32 = _get_rand (&seed) & 0x007FFFF0; + switch (left) { + case 7: + b |= ((uint64_t) ni[6]) << 48; + /* FALLTHRU */ + case 6: + b |= ((uint64_t) ni[5]) << 40; + /* FALLTHRU */ + case 5: + b |= ((uint64_t) ni[4]) << 32; + /* FALLTHRU */ + case 4: + b |= ((uint64_t) ni[3]) << 24; + /* FALLTHRU */ + case 3: + b |= ((uint64_t) ni[2]) << 16; + /* FALLTHRU */ + case 2: + b |= ((uint64_t) ni[1]) << 8; + /* FALLTHRU */ + case 1: + b |= ((uint64_t) ni[0]); + break; + default: + BSON_UNREACHABLE ("Invalid remainder during SipHash"); + case 0: + break; } - rand_bytes = _get_rand (&seed); - rand_bytes <<= 32; - rand_bytes |= _get_rand (&seed); + v3 ^= b; + + for (i = 0; i < C_ROUNDS; ++i) + _sip_round (&v0, &v1, &v2, &v3); + + v0 ^= b; + + v2 ^= 0xee; + + for (i = 0; i < D_ROUNDS; ++i) + _sip_round (&v0, &v1, &v2, &v3); + + b = v0 ^ v1 ^ v2 ^ v3; + _u64_into_u8x8_le (digest_buf, b); + + v1 ^= 0xdd; - /* Copy five random bytes, endianness does not matter. */ - memcpy (&context->rand, (char *) &rand_bytes, sizeof (context->rand)); + for (i = 0; i < D_ROUNDS; ++i) + _sip_round (&v0, &v1, &v2, &v3); + + b = v0 ^ v1 ^ v2 ^ v3; + _u64_into_u8x8_le (digest_buf + 8, b); + + memcpy (digest, digest_buf, sizeof digest_buf); +} + +/* + * The seed consists of the following hashed together: + * - current time (with microsecond resolution) + * - current pid + * - current hostname + * - The init-call counter + */ +struct _init_rand_params { + struct timeval time; + uint64_t pid; + char hostname[HOST_NAME_MAX]; + int64_t rand_call_counter; +}; + +static void +_bson_context_init_random (bson_context_t *context, bool init_seq) +{ + /* Keep an atomic counter of this function being called. This is used to add + * additional input to the random hash, ensuring no two calls in a single + * process will receive identical hash inputs, even occurring at the same + * microsecond. */ + static int64_t s_rand_call_counter = INT64_MIN; + + /* The message digest of the random params */ + uint64_t digest[2] = {0}; + uint64_t key[2] = {0}; + /* The randomness parameters */ + struct _init_rand_params rand_params; + + /* Init each part of the randomness source: */ + memset (&rand_params, 0, sizeof rand_params); + bson_gettimeofday (&rand_params.time); + rand_params.pid = _bson_getpid (); + _bson_context_get_hostname (rand_params.hostname); + rand_params.rand_call_counter = bson_atomic_int64_fetch_add ( + &s_rand_call_counter, 1, bson_memory_order_seq_cst); + + /* Generate a SipHash key. We do not care about secrecy or determinism, only + * uniqueness. */ + memcpy (key, &rand_params, sizeof key); + key[1] = ~key[0]; + + /* Hash the param struct */ + _siphash (&rand_params, sizeof rand_params, key, digest); + + /** Initialize the rand and sequence counters with our random digest */ + memcpy (context->randomness, digest, sizeof context->randomness); + if (init_seq) { + memcpy (&context->seq32, digest + 1, sizeof context->seq32); + memcpy (&context->seq64, digest + 1, sizeof context->seq64); + /* Chop off some initial bits for nicer counter behavior. This allows the + * low digit to start at a zero, and prevents immediately wrapping the + * counter in subsequent calls to set_oid_seq. */ + context->seq32 &= ~UINT32_C (0xf0000f); + context->seq64 &= ~UINT64_C (0xf0000f); + } + + /* Remember the PID we saw here. This may change in case of fork() */ + context->pid = rand_params.pid; } static void _bson_context_init (bson_context_t *context, bson_context_flags_t flags) { context->flags = (int) flags; - context->oid_set_seq32 = _bson_context_set_oid_seq32; - context->oid_set_seq64 = _bson_context_set_oid_seq64; - context->gethostname = _bson_context_get_hostname; + _bson_context_init_random (context, true /* Init counters */); +} - if ((flags & BSON_CONTEXT_THREAD_SAFE)) { - context->oid_set_seq32 = _bson_context_set_oid_seq32_threadsafe; - context->oid_set_seq64 = _bson_context_set_oid_seq64_threadsafe; - } - context->pid = _bson_getpid (); - _bson_context_init_random (context, true); +void +_bson_context_set_oid_rand (bson_context_t *context, bson_oid_t *oid) +{ + BSON_ASSERT (context); + BSON_ASSERT (oid); + + if (context->flags & BSON_CONTEXT_DISABLE_PID_CACHE) { + /* User has requested that we check if our PID has changed. This can occur + * after a call to fork() */ + uint64_t now_pid = _bson_getpid (); + if (now_pid != context->pid) { + _bson_context_init_random ( + context, false /* Do not update the sequence counters */); + } + } + /* Copy the stored randomness into the OID */ + memcpy (oid->bytes + BSON_OID_RANDOMESS_OFFSET, + &context->randomness, + BSON_OID_RANDOMNESS_SIZE); } -/* - *-------------------------------------------------------------------------- - * - * bson_context_new -- - * - * Initializes a new context with the flags specified. - * - * In most cases, you want to call this with @flags set to - * BSON_CONTEXT_NONE. - * - * If you are running on Linux, %BSON_CONTEXT_USE_TASK_ID can result - * in a healthy speedup for multi-threaded scenarios. - * - * If you absolutely must have a single context for your application - * and use more than one thread, then %BSON_CONTEXT_THREAD_SAFE should - * be bitwise-or'd with your flags. This requires synchronization - * between threads. - * - * If you expect your pid to change without notice, such as from an - * unexpected call to fork(), then specify - * %BSON_CONTEXT_DISABLE_PID_CACHE. - * - * Returns: - * A newly allocated bson_context_t that should be freed with - * bson_context_destroy(). - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - bson_context_t * bson_context_new (bson_context_flags_t flags) { @@ -382,23 +355,6 @@ bson_context_new (bson_context_flags_t flags) } -/* - *-------------------------------------------------------------------------- - * - * bson_context_destroy -- - * - * Cleans up a bson_context_t and releases any associated resources. - * This should be called when you are done using @context. - * - * Returns: - * None. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - void bson_context_destroy (bson_context_t *context) /* IN */ { @@ -408,32 +364,11 @@ bson_context_destroy (bson_context_t *context) /* IN */ static BSON_ONCE_FUN (_bson_context_init_default) { - _bson_context_init ( - &gContextDefault, - (BSON_CONTEXT_THREAD_SAFE | BSON_CONTEXT_DISABLE_PID_CACHE)); + _bson_context_init (&gContextDefault, BSON_CONTEXT_DISABLE_PID_CACHE); BSON_ONCE_RETURN; } -/* - *-------------------------------------------------------------------------- - * - * bson_context_get_default -- - * - * Fetches the default, thread-safe implementation of #bson_context_t. - * If you need faster generation, it is recommended you create your - * own #bson_context_t with bson_context_new(). - * - * Returns: - * A shared instance to the default #bson_context_t. This should not - * be modified or freed. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - bson_context_t * bson_context_get_default (void) { diff --git a/src/bson/bson-context.h b/src/bson/bson-context.h index 56e817f..3098ce6 100644 --- a/src/bson/bson-context.h +++ b/src/bson/bson-context.h @@ -28,10 +28,32 @@ BSON_BEGIN_DECLS +/** + * @brief Initialize a new context with the given flags + * + * @param flags Flags used to configure the behavior of the context. For most + * cases, this should be BSON_CONTEXT_NONE. + * + * @return A newly allocated context. Must be freed with bson_context_destroy() + * + * @note If you expect your pid to change without notice, such as from an + * unexpected call to fork(), then specify BSON_CONTEXT_DISABLE_PID_CACHE in + * `flags`. + */ BSON_EXPORT (bson_context_t *) bson_context_new (bson_context_flags_t flags); + +/** + * @brief Destroy and free a bson_context_t created by bson_context_new() + */ BSON_EXPORT (void) bson_context_destroy (bson_context_t *context); + +/** + * @brief Obtain a pointer to the application-default bson_context_t + * + * @note This context_t MUST NOT be passed to bson_context_destroy() + */ BSON_EXPORT (bson_context_t *) bson_context_get_default (void); diff --git a/src/bson/bson-decimal128.c b/src/bson/bson-decimal128.c index 56e3108..9293d19 100644 --- a/src/bson/bson-decimal128.c +++ b/src/bson/bson-decimal128.c @@ -19,6 +19,7 @@ #include #include +#include "bson-cmp.h" #include "bson-decimal128.h" #include "bson-types.h" #include "bson-macros.h" @@ -155,8 +156,6 @@ bson_decimal128_to_string (const bson_decimal128_t *dec, /* IN */ uint8_t significand_msb; /* the most signifcant significand bits (50-46) */ _bson_uint128_t significand128; /* temporary storage for significand decoding */ - size_t i; /* indexing variables */ - int j, k; memset (significand_str, 0, sizeof (significand_str)); @@ -212,7 +211,7 @@ bson_decimal128_to_string (const bson_decimal128_t *dec, /* IN */ */ is_zero = true; } else { - for (k = 3; k >= 0; k--) { + for (int k = 3; k >= 0; k--) { uint32_t least_digits = 0; _bson_uint128_divide1B ( significand128, &significand128, &least_digits); @@ -223,7 +222,7 @@ bson_decimal128_to_string (const bson_decimal128_t *dec, /* IN */ continue; } - for (j = 8; j >= 0; j--) { + for (int j = 8; j >= 0; j--) { significand[k * 9 + j] = least_digits % 10; least_digits /= 10; } @@ -264,7 +263,8 @@ bson_decimal128_to_string (const bson_decimal128_t *dec, /* IN */ *(str_out++) = '.'; } - for (i = 0; i < significand_digits && (str_out - str) < 36; i++) { + for (uint32_t i = 0; i < significand_digits && (str_out - str) < 36; + i++) { *(str_out++) = *(significand_read++) + '0'; } /* Exponent */ @@ -273,7 +273,8 @@ bson_decimal128_to_string (const bson_decimal128_t *dec, /* IN */ } else { /* Regular format with no decimal place */ if (exponent >= 0) { - for (i = 0; i < significand_digits && (str_out - str) < 36; i++) { + for (uint32_t i = 0; i < significand_digits && (str_out - str) < 36; + i++) { *(str_out++) = *(significand_read++) + '0'; } *str_out = '\0'; @@ -281,7 +282,7 @@ bson_decimal128_to_string (const bson_decimal128_t *dec, /* IN */ int32_t radix_position = significand_digits + exponent; if (radix_position > 0) { /* non-zero digits before radix */ - for (i = 0; + for (int32_t i = 0; i < radix_position && (str_out - str) < BSON_DECIMAL128_STRING; i++) { *(str_out++) = *(significand_read++) + '0'; @@ -295,8 +296,9 @@ bson_decimal128_to_string (const bson_decimal128_t *dec, /* IN */ *(str_out++) = '0'; } - for (i = 0; - (i < significand_digits - BSON_MAX (radix_position - 1, 0)) && + for (uint32_t i = 0; + bson_cmp_greater_us (significand_digits - i, + BSON_MAX (radix_position - 1, 0)) && (str_out - str) < BSON_DECIMAL128_STRING; i++) { *(str_out++) = *(significand_read++) + '0'; @@ -542,7 +544,7 @@ bson_decimal128_from_string_w_len (const char *string, /* IN */ continue; } - if (ndigits_stored < 34) { + if (ndigits_stored < BSON_DECIMAL128_MAX_DIGITS) { if (*str_read != '0' || found_nonzero) { if (!found_nonzero) { first_nonzero = ndigits_read; @@ -623,10 +625,12 @@ bson_decimal128_from_string_w_len (const char *string, /* IN */ /* to represent user input */ /* Overflow prevention */ - if (exponent <= radix_position && radix_position - exponent > (1 << 14)) { + if (bson_cmp_less_equal_su (exponent, radix_position) && + bson_cmp_greater_us (radix_position, exponent + (1 << 14))) { exponent = BSON_DECIMAL128_EXPONENT_MIN; } else { - exponent -= radix_position; + BSON_ASSERT (bson_in_range_unsigned (int32_t, radix_position)); + exponent -= (int32_t) radix_position; } /* Attempt to normalize the exponent */ @@ -634,7 +638,7 @@ bson_decimal128_from_string_w_len (const char *string, /* IN */ /* Shift exponent to significand and decrease */ last_digit++; - if (last_digit - first_digit > BSON_DECIMAL128_MAX_DIGITS) { + if (last_digit - first_digit >= BSON_DECIMAL128_MAX_DIGITS) { /* The exponent is too great to shift into the significand. */ if (significant_digits == 0) { /* Value is zero, we are allowed to clamp the exponent. */ diff --git a/src/bson/bson-dsl.h b/src/bson/bson-dsl.h new file mode 100644 index 0000000..9029978 --- /dev/null +++ b/src/bson/bson-dsl.h @@ -0,0 +1,1382 @@ +#ifndef BSON_BSON_DSL_H_INCLUDED +#define BSON_BSON_DSL_H_INCLUDED + +/** + * @file bson-dsl.h + * @brief Define a C-preprocessor DSL for working with BSON objects + * + * This file defines an embedded DSL for working with BSON objects consisely and + * correctly. + * + * For more information about using this DSL, refer to `bson-dsl.md`. + */ + +#include "bson.h" + +enum { + /// Toggle this value to enable/disable debug output for all bsonDSL + /// operations (printed to stderr). You can also set a constant + /// BSON_DSL_DEBUG within the scope of a DSL command to selectively debug + /// only the commands within that scope. + BSON_DSL_DEBUG = 0 +}; + +#define _bson_thread_local \ + BSON_IF_GNU_LIKE (__thread) BSON_IF_MSVC (__declspec(thread)) + +#define _bson_comdat \ + BSON_IF_WINDOWS (__declspec(selectany)) \ + BSON_IF_POSIX (__attribute__ ((weak))) + +#ifdef __GNUC__ +// GCC has a bug handling pragma statements that disable warnings within complex +// nested macro expansions. If we're GCC, just disable -Wshadow outright: +BSON_IF_GNU_LIKE (_Pragma ("GCC diagnostic ignored \"-Wshadow\"")) +#endif + +#define _bsonDSL_disableWarnings() \ + if (1) { \ + BSON_IF_GNU_LIKE (_Pragma ("GCC diagnostic push");) \ + BSON_IF_GNU_LIKE (_Pragma ("GCC diagnostic ignored \"-Wshadow\"");) \ + } else \ + ((void) 0) + +#define _bsonDSL_restoreWarnings() \ + if (1) { \ + BSON_IF_GNU_LIKE (_Pragma ("GCC diagnostic pop");) \ + } else \ + ((void) 0) + +/** + * @brief Parse the given BSON document. + * + * @param doc A bson_t object to walk. (Not a pointer) + */ +#define bsonParse(Document, ...) \ + _bsonDSL_begin ("bsonParse(%s)", _bsonDSL_str (Document)); \ + _bsonDSL_disableWarnings (); \ + bsonParseError = NULL; \ + BSON_MAYBE_UNUSED bool _bvHalt = false; \ + BSON_MAYBE_UNUSED const bool _bvContinue = false; \ + BSON_MAYBE_UNUSED const bool _bvBreak = false; \ + _bsonDSL_eval (_bsonParse ((Document), __VA_ARGS__)); \ + _bsonDSL_restoreWarnings (); \ + _bsonDSL_end + +/** + * @brief Visit each element of a BSON document + */ +#define bsonVisitEach(Document, ...) \ + _bsonDSL_begin ("bsonVisitEach(%s)", _bsonDSL_str (Document)); \ + _bsonDSL_disableWarnings (); \ + BSON_MAYBE_UNUSED bool _bvHalt = false; \ + _bsonDSL_eval (_bsonVisitEach ((Document), __VA_ARGS__)); \ + _bsonDSL_restoreWarnings (); \ + _bsonDSL_end + +#define bsonBuildContext (*_bsonBuildContextThreadLocalPtr) +#define bsonVisitContext (*_bsonVisitContextThreadLocalPtr) +#define bsonVisitIter (bsonVisitContext.iter) + +/// Begin any function-like macro by opening a new scope and writing a debug +/// message. +#define _bsonDSL_begin(Str, ...) \ + if (true) { \ + _bsonDSLDebug (Str, __VA_ARGS__); \ + ++_bson_dsl_indent + +/// End a function-like macro scope. +#define _bsonDSL_end \ + --_bson_dsl_indent; \ + } \ + else((void) 0) + +/** + * @brief Expands to a call to bson_append_{Kind}, with the three first + * arguments filled in by the DSL context variables. + */ +#define _bsonBuildAppendArgs \ + bsonBuildContext.doc, bsonBuildContext.key, bsonBuildContext.key_len + +/** + * The _bsonDocOperation_XYZ macros handle the top-level bsonBuild() + * items, and any nested doc() items, with XYZ being the doc-building + * subcommand. + */ +#define _bsonDocOperation(Command, _ignore, _count) \ + if (!bsonBuildError) { \ + _bsonDocOperation_##Command; \ + if (bsonBuildError) { \ + _bsonDSLDebug ("Stopping doc() due to bsonBuildError: [%s]", \ + bsonBuildError); \ + } \ + } + +#define _bsonValueOperation(P) _bsonValueOperation_##P + +/// key-value pair with explicit key length +#define _bsonDocOperation_kvl(String, Len, Element) \ + _bsonDSL_begin ("\"%s\" => [%s]", String, _bsonDSL_strElide (30, Element)); \ + const char *_bbString = (String); \ + const uint64_t length = (Len); \ + if (bson_in_range_unsigned (int, length)) { \ + _bbCtx.key = _bbString; \ + _bbCtx.key_len = (int) length; \ + _bsonValueOperation (Element); \ + } else { \ + bsonBuildError = "Out-of-range key string length value"; \ + } \ + _bsonDSL_end + +/// Key-value pair with a C-string +#define _bsonDocOperation_kv(String, Element) \ + _bsonDocOperation_kvl ((String), strlen ((String)), Element) + +/// Execute arbitrary code +#define _bsonDocOperation_do(...) \ + _bsonDSL_begin ("do(%s)", _bsonDSL_strElide (30, __VA_ARGS__)); \ + do { \ + __VA_ARGS__; \ + } while (0); \ + if (bsonBuildError) { \ + _bsonDSLDebug ("do() set bsonBuildError: [%s]", bsonBuildError); \ + } \ + _bsonDSL_end + +/// We must defer expansion of the nested doc() to allow "recursive" evaluation +#define _bsonValueOperation_doc \ + _bsonValueOperationDeferred_doc _bsonDSL_nothing () +#define _bsonArrayOperation_doc(...) _bsonArrayAppendValue (doc (__VA_ARGS__)) + +#define _bsonValueOperationDeferred_doc(...) \ + _bsonDSL_begin ("doc(%s)", _bsonDSL_strElide (30, __VA_ARGS__)); \ + /* Write to this variable as the child: */ \ + bson_t _bbChildDoc = BSON_INITIALIZER; \ + if (!bson_append_document_begin (_bsonBuildAppendArgs, &_bbChildDoc)) { \ + bsonBuildError = \ + "Error while initializing child document: " _bsonDSL_str ( \ + __VA_ARGS__); \ + } else { \ + _bsonBuildAppend (_bbChildDoc, __VA_ARGS__); \ + if (!bsonBuildError) { \ + if (!bson_append_document_end (bsonBuildContext.doc, &_bbChildDoc)) { \ + bsonBuildError = \ + "Error while finalizing document: " _bsonDSL_str (__VA_ARGS__); \ + } \ + } \ + } \ + _bsonDSL_end + +/// We must defer expansion of the nested array() to allow "recursive" +/// evaluation +#define _bsonValueOperation_array \ + _bsonValueOperationDeferred_array _bsonDSL_nothing () +#define _bsonArrayOperation_array(...) \ + _bsonArrayAppendValue (array (__VA_ARGS__)) + +#define _bsonValueOperationDeferred_array(...) \ + _bsonDSL_begin ("array(%s)", _bsonDSL_strElide (30, __VA_ARGS__)); \ + /* Write to this variable as the child array: */ \ + bson_t _bbArray = BSON_INITIALIZER; \ + if (!bson_append_array_begin (_bsonBuildAppendArgs, &_bbArray)) { \ + bsonBuildError = \ + "Error while initializing child array: " _bsonDSL_str (__VA_ARGS__); \ + } else { \ + _bsonBuildArray (_bbArray, __VA_ARGS__); \ + if (!bsonBuildError) { \ + if (!bson_append_array_end (bsonBuildContext.doc, &_bbArray)) { \ + bsonBuildError = \ + "Error while finalizing child array: " _bsonDSL_str ( \ + __VA_ARGS__); \ + } \ + } else { \ + _bsonDSLDebug ("Got bsonBuildError: [%s]", bsonBuildError); \ + } \ + } \ + _bsonDSL_end + +/// Append a UTF-8 string with an explicit length +#define _bsonValueOperation_utf8_w_len(String, Len) \ + if (!bson_append_utf8 (_bsonBuildAppendArgs, (String), (int) (Len))) { \ + bsonBuildError = \ + "Error while appending utf8 string: " _bsonDSL_str (String); \ + } else \ + ((void) 0) +#define _bsonArrayOperation_utf8_w_len(X) _bsonArrayAppendValue (utf8_w_len (X)) + +/// Append a "cstr" as UTF-8 +#define _bsonValueOperation_cstr(String) \ + _bsonValueOperation_utf8_w_len ((String), strlen (String)) +#define _bsonArrayOperation_cstr(X) _bsonArrayAppendValue (cstr (X)) + +/// Append an int32 +#define _bsonValueOperation_int32(Integer) \ + if (!bson_append_int32 (_bsonBuildAppendArgs, (Integer))) { \ + bsonBuildError = \ + "Error while appending int32(" _bsonDSL_str (Integer) ")"; \ + } else \ + ((void) 0) +#define _bsonArrayOperation_int32(X) _bsonArrayAppendValue (int32 (X)) + +/// Append an int64 +#define _bsonValueOperation_int64(Integer) \ + if (!bson_append_int64 (_bsonBuildAppendArgs, (Integer))) { \ + bsonBuildError = \ + "Error while appending int64(" _bsonDSL_str (Integer) ")"; \ + } else \ + ((void) 0) +#define _bsonArrayOperation_int64(X) _bsonArrayAppendValue (int64 (X)) + +/// Append the value referenced by a given iterator +#define _bsonValueOperation_iterValue(Iter) \ + if (!bson_append_iter (_bsonBuildAppendArgs, &(Iter))) { \ + bsonBuildError = \ + "Error while appending iterValue(" _bsonDSL_str (Iter) ")"; \ + } else \ + ((void) 0) +#define _bsonArrayOperation_iterValue(X) _bsonArrayAppendValue (iterValue (X)) + +/// Append the BSON document referenced by the given pointer +#define _bsonValueOperation_bson(Doc) \ + if (!bson_append_document (_bsonBuildAppendArgs, &(Doc))) { \ + bsonBuildError = \ + "Error while appending subdocument: bson(" _bsonDSL_str (Doc) ")"; \ + } else \ + ((void) 0) +#define _bsonArrayOperation_bson(X) _bsonArrayAppendValue (bson (X)) + +/// Append the BSON document referenced by the given pointer as an array +#define _bsonValueOperation_bsonArray(Arr) \ + if (!bson_append_array (_bsonBuildAppendArgs, &(Arr))) { \ + bsonBuildError = "Error while appending subdocument array: " \ + "bsonArray(" _bsonDSL_str (Arr) ")"; \ + } else \ + ((void) 0) +#define _bsonArrayOperation_bsonArray(X) _bsonArrayAppendValue (bsonArray (X)) + +#define _bsonValueOperation_bool(b) \ + if (!bson_append_bool (_bsonBuildAppendArgs, (b))) { \ + bsonBuildError = "Error while appending bool(" _bsonDSL_str (b) ")"; \ + } else \ + ((void) 0) +#define _bsonArrayOperation_bool(X) _bsonArrayAppendValue (bool (X)) +#define _bsonValueOperation__Bool(b) _bsonValueOperation_bool (b) +#define _bsonArrayOperation__Bool(X) _bsonArrayAppendValue (_Bool (X)) + +#define _bsonValueOperation_null \ + if (!bson_append_null (_bsonBuildAppendArgs)) { \ + bsonBuildError = "Error while appending a null"; \ + } else \ + ((void) 0) +#define _bsonArrayOperation_null _bsonValueOperation (null) + +#define _bsonArrayOperation_value(X) _bsonArrayAppendValue (value (X)) + +#define _bsonValueOperation_value(Value) \ + _bsonDSL_begin ("value(%s)", _bsonDSL_str (Value)); \ + if (!bson_append_value (_bsonBuildAppendArgs, &(Value))) { \ + bsonBuildError = \ + "Error while appending value(" _bsonDSL_str (Value) ")"; \ + } \ + _bsonDSL_end + +/// Insert the given BSON document into the parent document in-place +#define _bsonDocOperation_insert(OtherBSON, Pred) \ + _bsonDSL_begin ("Insert other document: [%s]", _bsonDSL_str (OtherBSON)); \ + const bool _bvHalt = false; /* Required for _bsonVisitEach() */ \ + _bsonVisitEach ( \ + OtherBSON, \ + if (Pred, then (do(_bsonDocOperation_iterElement (bsonVisitIter))))); \ + _bsonDSL_end + +#define _bsonDocOperation_insertFromIter(Iter, Pred) \ + _bsonDSL_begin ("Insert document from iterator: [%s]", \ + _bsonDSL_str (Iter)); \ + bson_t _bbDocFromIter = _bson_dsl_iter_as_doc (&(Iter)); \ + if (_bbDocFromIter.len == 0) { \ + _bsonDSLDebug ( \ + "NOTE: Skipping insert of non-document value from iterator"); \ + } else { \ + _bsonDocOperation_insert (_bbDocFromIter, Pred); \ + } \ + _bsonDSL_end + +#define _bsonDocOperation_iterElement(Iter) \ + _bsonDSL_begin ("Insert element from bson_iter_t [%s]", \ + _bsonDSL_str (Iter)); \ + bson_iter_t _bbIter = (Iter); \ + _bsonDocOperation_kvl (bson_iter_key (&_bbIter), \ + bson_iter_key_len (&_bbIter), \ + iterValue (_bbIter)); \ + _bsonDSL_end + +/// Insert the given BSON document into the parent array. Keys of the given +/// document are discarded and it is treated as an array of values. +#define _bsonArrayOperation_insert(OtherArr, Pred) \ + _bsonDSL_begin ("Insert other array: [%s]", _bsonDSL_str (OtherArr)); \ + _bsonVisitEach ( \ + OtherArr, \ + if (Pred, then (do(_bsonArrayOperation_iterValue (bsonVisitIter))))); \ + _bsonDSL_end + +#define _bsonArrayAppendValue(ValueOperation) \ + _bsonDSL_begin ("[%d] => [%s]", \ + (int) bsonBuildContext.index, \ + _bsonDSL_strElide (30, ValueOperation)); \ + /* Set the doc key to the array index as a string: */ \ + _bsonBuild_setKeyToArrayIndex (bsonBuildContext.index); \ + /* Append a value: */ \ + _bsonValueOperation_##ValueOperation; \ + /* Increment the array index: */ \ + ++_bbCtx.index; \ + _bsonDSL_end + + +#define _bsonDocOperationIfThen_then _bsonBuildAppendWithCurrentContext +#define _bsonDocOperationIfElse_else _bsonBuildAppendWithCurrentContext + +#define _bsonDocOperationIfThenElse(Condition, Then, Else) \ + if ((Condition)) { \ + _bsonDSLDebug ("Taking TRUE branch: [%s]", _bsonDSL_str (Then)); \ + _bsonDocOperationIfThen_##Then; \ + } else { \ + _bsonDSLDebug ("Taking FALSE branch: [%s]", _bsonDSL_str (Else)); \ + _bsonDocOperationIfElse_##Else; \ + } + +#define _bsonDocOperationIfThen(Condition, Then) \ + if ((Condition)) { \ + _bsonDSLDebug ("Taking TRUE branch: [%s]", _bsonDSL_str (Then)); \ + _bsonDocOperationIfThen_##Then; \ + } + +#define _bsonDocOperation_if(Condition, ...) \ + _bsonDSL_begin ("Conditional append on [%s]", _bsonDSL_str (Condition)); \ + /* Pick a sub-macro depending on if there are one or two args */ \ + _bsonDSL_ifElse (_bsonDSL_hasComma (__VA_ARGS__), \ + _bsonDocOperationIfThenElse, \ + _bsonDocOperationIfThen) (Condition, __VA_ARGS__); \ + _bsonDSL_end + +#define _bsonArrayOperationIfThen_then _bsonBuildArrayWithCurrentContext +#define _bsonArrayOperationIfElse_else _bsonBuildArrayWithCurrentContext + +#define _bsonArrayOperationIfThenElse(Condition, Then, Else) \ + if ((Condition)) { \ + _bsonDSLDebug ("Taking TRUE branch: [%s]", _bsonDSL_str (Then)); \ + _bsonArrayOperationIfThen_##Then; \ + } else { \ + _bsonDSLDebug ("Taking FALSE branch: [%s]", _bsonDSL_str (Else)); \ + _bsonArrayOperationIfElse_##Else; \ + } + +#define _bsonArrayOperationIfThen(Condition, Then) \ + if ((Condition)) { \ + _bsonDSLDebug ("Taking TRUE branch: [%s]", _bsonDSL_str (Then)); \ + _bsonArrayOperationIfThen_##Then; \ + } + +#define _bsonArrayOperation_if(Condition, ...) \ + _bsonDSL_begin ("Conditional value on [%s]", _bsonDSL_str (Condition)); \ + /* Pick a sub-macro depending on if there are one or two args */ \ + _bsonDSL_ifElse (_bsonDSL_hasComma (__VA_ARGS__), \ + _bsonArrayOperationIfThenElse, \ + _bsonArrayOperationIfThen) (Condition, __VA_ARGS__); \ + _bsonDSL_end + +#define _bsonValueOperationIf_then(X) _bsonValueOperation_##X +#define _bsonValueOperationIf_else(X) _bsonValueOperation_##X + +#define _bsonValueOperation_if(Condition, Then, Else) \ + if ((Condition)) { \ + _bsonDSLDebug ("Taking TRUE branch: [%s]", _bsonDSL_str (Then)); \ + _bsonValueOperationIf_##Then; \ + } else { \ + _bsonDSLDebug ("Taking FALSE branch: [%s]", _bsonDSL_str (Else)); \ + _bsonValueOperationIf_##Else; \ + } + +#define _bsonBuild_setKeyToArrayIndex(Idx) \ + _bbCtx.key_len = bson_snprintf ( \ + _bbCtx.index_key_str, sizeof _bbCtx.index_key_str, "%d",(int) _bbCtx.index); \ + _bbCtx.key = _bbCtx.index_key_str + +/// Handle an element of array() +#define _bsonArrayOperation(Element, _nil, _count) \ + if (!bsonBuildError) { \ + _bsonArrayOperation_##Element; \ + } + +#define _bsonBuildAppendWithCurrentContext(...) \ + _bsonDSL_mapMacro (_bsonDocOperation, ~, __VA_ARGS__) + +#define _bsonBuildArrayWithCurrentContext(...) \ + _bsonDSL_mapMacro (_bsonArrayOperation, ~, __VA_ARGS__) + +#define _bsonDSL_Type_double BSON_TYPE_DOUBLE +#define _bsonDSL_Type_utf8 BSON_TYPE_UTF8 +#define _bsonDSL_Type_doc BSON_TYPE_DOCUMENT +#define _bsonDSL_Type_array BSON_TYPE_ARRAY +#define _bsonDSL_Type_binary BSON_TYPE_BINARY +#define _bsonDSL_Type_undefined BSON_TYPE_UNDEFINED +#define _bsonDSL_Type_oid BSON_TYPE_OID +#define _bsonDSL_Type_bool BSON_TYPE_BOOL +// ("bool" may be spelled _Bool due to macro expansion:) +#define _bsonDSL_Type__Bool BSON_TYPE_BOOL +#define _bsonDSL_Type_date_time BSON_TYPE_DATE_TIME +#define _bsonDSL_Type_null BSON_TYPE_NULL +#define _bsonDSL_Type_regex BSON_TYPE_REGEX +#define _bsonDSL_Type_dbpointer BSON_TYPE_DBPOINTER +#define _bsonDSL_Type_code BSON_TYPE_CODE +#define _bsonDSL_Type_codewscope BSON_TYPE_CODEWSCOPE +#define _bsonDSL_Type_int32 BSON_TYPE_INT32 +#define _bsonDSL_Type_timestamp BSON_TYPE_TIMESTAMP +#define _bsonDSL_Type_int64 BSON_TYPE_INT64 +#define _bsonDSL_Type_decimal128 BSON_TYPE_DECIMAL128 + +#define _bsonDSL_Type_string __NOTE__No_type_named__string__did_you_mean__utf8 + +#define _bsonVisitOperation_halt _bvHalt = true + +#define _bsonVisitOperation_if(Predicate, ...) \ + _bsonDSL_begin ("if(%s)", _bsonDSL_str (Predicate)); \ + _bsonDSL_ifElse (_bsonDSL_hasComma (__VA_ARGS__), \ + _bsonVisit_ifThenElse, \ + _bsonVisit_ifThen) (Predicate, __VA_ARGS__); \ + _bsonDSL_end + +#define _bsonVisit_ifThenElse(Predicate, Then, Else) \ + if (bsonPredicate (Predicate)) { \ + _bsonDSLDebug ("then:"); \ + _bsonVisit_ifThen_##Then; \ + } else { \ + _bsonDSLDebug ("else:"); \ + _bsonVisit_ifElse_##Else; \ + } + +#define _bsonVisit_ifThen(Predicate, Then) \ + if (bsonPredicate (Predicate)) { \ + _bsonDSLDebug ("then:"); \ + _bsonVisit_ifThen_##Then; \ + } else { \ + _bsonDSLDebug ("[else nothing]"); \ + } + +#define _bsonVisit_ifThen_then _bsonVisit_applyOps +#define _bsonVisit_ifElse_else _bsonVisit_applyOps + +#define _bsonVisitOperation_storeBool(Dest) \ + _bsonDSL_begin ("storeBool(%s)", _bsonDSL_str (Dest)); \ + (Dest) = bson_iter_as_bool (&bsonVisitIter); \ + _bsonDSL_end + +#define _bsonVisitOperation_storeStrRef(Dest) \ + _bsonDSL_begin ("storeStrRef(%s)", _bsonDSL_str (Dest)); \ + (Dest) = bson_iter_utf8 (&bsonVisitIter, NULL); \ + _bsonDSL_end + +#define _bsonVisitOperation_storeStrDup(Dest) \ + _bsonDSL_begin ("storeStrDup(%s)", _bsonDSL_str (Dest)); \ + (Dest) = bson_iter_dup_utf8 (&bsonVisitIter, NULL); \ + _bsonDSL_end + +#define _bsonVisitOperation_storeDocDup(Dest) \ + _bsonDSL_begin ("storeDocDup(%s)", _bsonDSL_str (Dest)); \ + bson_t _bvDoc = BSON_INITIALIZER; \ + _bson_dsl_iter_as_doc (&_bvDoc, &bsonVisitIter); \ + if (_bvDoc.len) { \ + bson_copy_to (&_bvDoc, &(Dest)); \ + } \ + _bsonDSL_end + +#define _bsonVisitOperation_storeDocRef(Dest) \ + _bsonDSL_begin ("storeDocRef(%s)", _bsonDSL_str (Dest)); \ + _bson_dsl_iter_as_doc (&(Dest), &bsonVisitIter); \ + _bsonDSL_end + +#define _bsonVisitOperation_storeDocDupPtr(Dest) \ + _bsonDSL_begin ("storeDocDupPtr(%s)", _bsonDSL_str (Dest)); \ + bson_t _bvDoc = BSON_INITIALIZER; \ + _bson_dsl_iter_as_doc (&_bvDoc, &bsonVisitIter); \ + if (_bvDoc.len) { \ + (Dest) = bson_copy (&_bvDoc); \ + } \ + _bsonDSL_end + +#define _bsonVisitOperation_storeInt32(Dest) \ + _bsonDSL_begin ("storeInt32(%s)", _bsonDSL_str (Dest)); \ + (Dest) = bson_iter_int32 (&bsonVisitIter); \ + _bsonDSL_end + +#define _bsonVisitOperation_do(...) \ + _bsonDSL_begin ("do: %s", _bsonDSL_strElide (30, __VA_ARGS__)); \ + do { \ + __VA_ARGS__; \ + } while (0); \ + _bsonDSL_end + +#define _bsonVisitOperation_appendTo(BSON) \ + _bsonDSL_begin ("appendTo(%s)", _bsonDSL_str (BSON)); \ + if (!bson_append_iter (&(BSON), \ + bson_iter_key (&bsonVisitIter), \ + (int) bson_iter_key_len (&bsonVisitIter), \ + &bsonVisitIter)) { \ + bsonParseError = "Error in appendTo(" _bsonDSL_str (BSON) ")"; \ + } \ + _bsonDSL_end + +#define _bsonVisitCase_when(Pred, ...) \ + _bsonDSL_begin ("when: [%s]", _bsonDSL_str (Pred)); \ + _bvCaseMatched = _bsonPredicate (Pred); \ + if (_bvCaseMatched) { \ + _bsonVisit_applyOps (__VA_ARGS__); \ + } \ + _bsonDSL_end + +#define _bsonVisitCase_else(...) \ + _bsonDSL_begin ("else:%s", ""); \ + _bvCaseMatched = true; \ + _bsonVisit_applyOps (__VA_ARGS__); \ + _bsonDSL_end + +#define _bsonVisitCase(Pair, _nil, _count) \ + if (!_bvCaseMatched) { \ + _bsonVisitCase_##Pair; \ + } else \ + ((void) 0); + +#define _bsonVisitOperation_case(...) \ + _bsonDSL_begin ("case:%s", ""); \ + BSON_MAYBE_UNUSED bool _bvCaseMatched = false; \ + _bsonDSL_mapMacro (_bsonVisitCase, ~, __VA_ARGS__); \ + _bsonDSL_end + +#define _bsonVisitOperation_append \ + _bsonVisitOneApplyDeferred_append _bsonDSL_nothing () +#define _bsonVisitOneApplyDeferred_append(Doc, ...) \ + _bsonDSL_begin ("append to [%s] : %s", \ + _bsonDSL_str (Doc), \ + _bsonDSL_strElide (30, __VA_ARGS__)); \ + _bsonBuildAppend (Doc, __VA_ARGS__); \ + if (bsonBuildError) { \ + bsonParseError = bsonBuildError; \ + } \ + _bsonDSL_end + +#define _bsonVisitEach(Doc, ...) \ + _bsonDSL_begin ("visitEach(%s)", _bsonDSL_str (Doc)); \ + do { \ + /* Reset the context */ \ + struct _bsonVisitContext_t _bvCtx = { \ + .doc = &(Doc), \ + .parent = _bsonVisitContextThreadLocalPtr, \ + .index = 0, \ + }; \ + _bsonVisitContextThreadLocalPtr = &_bvCtx; \ + bsonParseError = NULL; \ + /* Iterate over each element of the document */ \ + if (!bson_iter_init (&_bvCtx.iter, &(Doc))) { \ + bsonParseError = "Invalid BSON data [a]"; \ + } \ + BSON_MAYBE_UNUSED bool _bvBreak = false; \ + BSON_MAYBE_UNUSED bool _bvContinue = false; \ + while (bson_iter_next (&_bvCtx.iter) && !_bvHalt && !bsonParseError && \ + !_bvBreak) { \ + _bvContinue = false; \ + _bsonVisit_applyOps (__VA_ARGS__); \ + ++_bvCtx.index; \ + } \ + if (bsonVisitIter.err_off) { \ + bsonParseError = "Invalid BSON data [b]"; \ + } \ + /* Restore the dsl context */ \ + _bsonVisitContextThreadLocalPtr = _bvCtx.parent; \ + } while (0); \ + _bsonDSL_end + +#define _bsonVisitOperation_visitEach \ + _bsonVisitOperation_visitEachDeferred _bsonDSL_nothing () +#define _bsonVisitOperation_visitEachDeferred(...) \ + _bsonDSL_begin ("visitEach:%s", ""); \ + do { \ + const uint8_t *data; \ + uint32_t len; \ + bson_type_t typ = bson_iter_type_unsafe (&bsonVisitIter); \ + if (typ == BSON_TYPE_ARRAY) \ + bson_iter_array (&bsonVisitIter, &len, &data); \ + else if (typ == BSON_TYPE_DOCUMENT) \ + bson_iter_document (&bsonVisitIter, &len, &data); \ + else { \ + _bsonDSLDebug ("(Skipping visitEach() of non-array/document value)"); \ + break; \ + } \ + bson_t inner; \ + bson_init_static (&inner, data, len); \ + _bsonVisitEach (inner, __VA_ARGS__); \ + } while (0); \ + _bsonDSL_end + +#define _bsonVisitOperation_nop _bsonDSLDebug ("[nop]") +#define _bsonVisitOperation_parse(...) \ + do { \ + const uint8_t *data; \ + uint32_t len; \ + bson_type_t typ = bson_iter_type (&bsonVisitIter); \ + if (typ == BSON_TYPE_ARRAY) \ + bson_iter_array (&bsonVisitIter, &len, &data); \ + else if (typ == BSON_TYPE_DOCUMENT) \ + bson_iter_document (&bsonVisitIter, &len, &data); \ + else { \ + _bsonDSLDebug ("Ignoring parse() for non-document/array value"); \ + break; \ + } \ + bson_t inner; \ + bson_init_static (&inner, data, len); \ + _bsonParse (inner, __VA_ARGS__); \ + } while (0); + +#define _bsonVisitOperation_continue _bvContinue = true +#define _bsonVisitOperation_break _bvBreak = _bvContinue = true +#define _bsonVisitOperation_require(Predicate) \ + _bsonDSL_begin ("require(%s)", _bsonDSL_str (Predicate)); \ + if (!bsonPredicate (Predicate)) { \ + bsonParseError = \ + "Element requirement failed: " _bsonDSL_str (Predicate); \ + } \ + _bsonDSL_end + +#define _bsonVisitOperation_error(S) bsonParseError = (S) +#define _bsonVisitOperation_errorf(S, ...) \ + (bsonParseError = _bson_dsl_errorf (&(S), __VA_ARGS__)) +#define _bsonVisitOperation_dupPath(S) \ + _bsonDSL_begin ("dupPath(%s)", _bsonDSL_str (S)); \ + _bson_dsl_dupPath (&(S)); \ + _bsonDSL_end + +#define _bsonVisit_applyOp(P, _const, _count) \ + do { \ + if (!_bvContinue && !_bvHalt && !bsonParseError) { \ + _bsonVisitOperation_##P; \ + } \ + } while (0); + +#define _bsonParse(Doc, ...) \ + do { \ + BSON_MAYBE_UNUSED const bson_t *_bpDoc = &(Doc); \ + /* Keep track of which elements have been visited based on their index*/ \ + uint64_t _bpVisitBits_static[4] = {0}; \ + BSON_MAYBE_UNUSED uint64_t *_bpVisitBits = _bpVisitBits_static; \ + BSON_MAYBE_UNUSED size_t _bpNumVisitBitInts = \ + sizeof _bpVisitBits_static / sizeof (uint64_t); \ + BSON_MAYBE_UNUSED bool _bpFoundElement = false; \ + _bsonParse_applyOps (__VA_ARGS__); \ + /* We may have allocated for visit bits */ \ + if (_bpVisitBits != _bpVisitBits_static) { \ + bson_free (_bpVisitBits); \ + } \ + } while (0) + +#define _bsonParse_applyOps(...) \ + _bsonDSL_mapMacro (_bsonParse_applyOp, ~, __VA_ARGS__) + +/// Parse one entry referrenced by the context iterator +#define _bsonParse_applyOp(P, _nil, Counter) \ + do { \ + if (!_bvHalt && !bsonParseError) { \ + _bsonParseOperation_##P; \ + } \ + } while (0); + +#define _bsonParseMarkVisited(Index) \ + if (1) { \ + const size_t nth_int = Index / 64u; \ + const size_t nth_bit = Index % 64u; \ + while (nth_int >= _bpNumVisitBitInts) { \ + /* Say that five times, fast: */ \ + size_t new_num_visit_bit_ints = _bpNumVisitBitInts * 2u; \ + uint64_t *new_visit_bit_ints = \ + bson_malloc0 (sizeof (uint64_t) * new_num_visit_bit_ints); \ + memcpy (new_visit_bit_ints, \ + _bpVisitBits, \ + sizeof (uint64_t) * _bpNumVisitBitInts); \ + if (_bpVisitBits != _bpVisitBits_static) { \ + bson_free (_bpVisitBits); \ + } \ + _bpVisitBits = new_visit_bit_ints; \ + _bpNumVisitBitInts = new_num_visit_bit_ints; \ + } \ + \ + _bpVisitBits[nth_int] |= (UINT64_C (1) << nth_bit); \ + } else \ + ((void) 0) + +#define _bsonParseDidVisitNth(Index) \ + _bsonParseDidVisitNth_1 (Index / 64u, Index % 64u) +#define _bsonParseDidVisitNth_1(NthInt, NthBit) \ + (NthInt < _bpNumVisitBitInts && \ + (_bpVisitBits[NthInt] & (UINT64_C (1) << NthBit))) + +#define _bsonParseOperation_find(Predicate, ...) \ + _bsonDSL_begin ("find(%s)", _bsonDSL_str (Predicate)); \ + _bpFoundElement = false; \ + _bsonVisitEach ( \ + *_bpDoc, \ + if (Predicate, \ + then (do(_bsonParseMarkVisited (bsonVisitContext.index); \ + _bpFoundElement = true), \ + __VA_ARGS__, \ + break))); \ + if (!_bpFoundElement && !bsonParseError) { \ + _bsonDSLDebug ("[not found]"); \ + } \ + _bsonDSL_end + +#define _bsonParseOperation_require(Predicate, ...) \ + _bsonDSL_begin ("require(%s)", _bsonDSL_str (Predicate)); \ + _bpFoundElement = false; \ + _bsonVisitEach ( \ + *_bpDoc, \ + if (Predicate, \ + then (do(_bsonParseMarkVisited (bsonVisitContext.index); \ + _bpFoundElement = true), \ + __VA_ARGS__, \ + break))); \ + if (!_bpFoundElement && !bsonParseError) { \ + bsonParseError = \ + "Failed to find a required element: " _bsonDSL_str (Predicate); \ + } \ + _bsonDSL_end + +#define _bsonParseOperation_visitOthers(...) \ + _bsonDSL_begin ("visitOthers(%s)", _bsonDSL_strElide (30, __VA_ARGS__)); \ + _bsonVisitEach ( \ + *_bpDoc, \ + if (not(eval (_bsonParseDidVisitNth (bsonVisitContext.index))), \ + then (__VA_ARGS__))); \ + _bsonDSL_end + +#define bsonPredicate(P) _bsonPredicate _bsonDSL_nothing () (P) +#define _bsonPredicate(P) _bsonPredicate_Condition_##P + +#define _bsonPredicate_Condition_ \ + __NOTE__Missing_name_for_a_predicate_expression + +#define _bsonPredicate_Condition_allOf(...) \ + (1 _bsonDSL_mapMacro (_bsonPredicateAnd, ~, __VA_ARGS__)) +#define _bsonPredicate_Condition_anyOf(...) \ + (0 _bsonDSL_mapMacro (_bsonPredicateOr, ~, __VA_ARGS__)) +#define _bsonPredicate_Condition_not(...) \ + (!(0 _bsonDSL_mapMacro (_bsonPredicateOr, ~, __VA_ARGS__))) +#define _bsonPredicateAnd(Pred, _ignore, _ignore1) \ + &&_bsonPredicate _bsonDSL_nothing () (Pred) +#define _bsonPredicateOr(Pred, _ignore, _ignore2) \ + || _bsonPredicate _bsonDSL_nothing () (Pred) + +#define _bsonPredicate_Condition_eval(X) (X) + +#define _bsonPredicate_Condition_key(...) \ + (_bson_dsl_key_is_anyof (bson_iter_key (&bsonVisitIter), \ + bson_iter_key_len (&bsonVisitIter), \ + true /* case senstive */, \ + __VA_ARGS__, \ + NULL)) + +#define _bsonPredicate_Condition_iKey(...) \ + (_bson_dsl_key_is_anyof (bson_iter_key (&bsonVisitIter), \ + bson_iter_key_len (&bsonVisitIter), \ + false /* case insenstive */, \ + __VA_ARGS__, \ + NULL)) + +#define _bsonPredicate_Condition_type(Type) \ + (bson_iter_type (&bsonVisitIter) == _bsonDSL_Type_##Type) + +#define _bsonPredicate_Condition_keyWithType(Key, Type) \ + (_bsonPredicate_Condition_allOf _bsonDSL_nothing () (key (Key), type (Type))) + +#define _bsonPredicate_Condition_iKeyWithType(Key, Type) \ + (_bsonPredicate_Condition_allOf _bsonDSL_nothing () (iKey (Key), \ + type (Type))) + +#define _bsonPredicate_Condition_lastElement \ + (_bson_dsl_iter_is_last_element (&bsonVisitIter)) + +#define _bsonPredicate_Condition_isNumeric \ + BSON_ITER_HOLDS_NUMBER (&bsonVisitIter) + +#define _bsonPredicate_Condition_1 1 +#define _bsonPredicate_Condition_0 0 +#define _bsonPredicate_Condition_true true +#define _bsonPredicate_Condition_false false + +#define _bsonPredicate_Condition_isTrue (bson_iter_as_bool (&bsonVisitIter)) +#define _bsonPredicate_Condition_isFalse (!bson_iter_as_bool (&bsonVisitIter)) +#define _bsonPredicate_Condition_empty \ + (_bson_dsl_is_empty_bson (&bsonVisitIter)) + +#define _bsonPredicate_Condition_strEqual(S) (_bson_dsl_test_strequal (S, true)) +#define _bsonPredicate_Condition_iStrEqual(S) \ + (_bson_dsl_test_strequal (S, false)) + +#define _bsonPredicate_Condition_eq(Type, Value) \ + (_bsonPredicate_Condition_type (Type) && bsonAs (Type) == Value) + +#define _bsonParseOperation_else _bsonParse_deferredElse _bsonDSL_nothing () +#define _bsonParse_deferredElse(...) \ + if (!_bpFoundElement) { \ + _bsonDSL_begin ("else:%s", ""); \ + _bsonParse_applyOps (__VA_ARGS__); \ + _bsonDSL_end; \ + } else \ + ((void) 0) + +#define _bsonParseOperation_do(...) \ + _bsonDSL_begin ("do: %s", _bsonDSL_strElide (30, __VA_ARGS__)); \ + do { \ + __VA_ARGS__; \ + } while (0); \ + _bsonDSL_end + +#define _bsonParseOperation_halt _bvHalt = true + +#define _bsonParseOperation_error(S) bsonParseError = (S) +#define _bsonParseOperation_errorf(S, ...) \ + (bsonParseError = _bson_dsl_errorf (&(S), __VA_ARGS__)) + +/// Perform conditional parsing +#define _bsonParseOperation_if(Condition, ...) \ + _bsonDSL_begin ("if(%s)", _bsonDSL_str (Condition)); \ + /* Pick a sub-macro depending on if there are one or two args */ \ + _bsonDSL_ifElse (_bsonDSL_hasComma (__VA_ARGS__), \ + _bsonParse_ifThenElse, \ + _bsonParse_ifThen) (Condition, __VA_ARGS__); \ + _bsonDSL_end + +#define _bsonParse_ifThen_then _bsonParse_applyOps +#define _bsonParse_ifElse_else _bsonParse_applyOps + +#define _bsonParse_ifThenElse(Condition, Then, Else) \ + if ((Condition)) { \ + _bsonDSLDebug ("then:"); \ + _bsonParse_ifThen_##Then; \ + } else { \ + _bsonDSLDebug ("else:"); \ + _bsonParse_ifElse_##Else; \ + } + +#define _bsonParse_ifThen(Condition, Then) \ + if ((Condition)) { \ + _bsonDSLDebug ("%s", _bsonDSL_str (Then)); \ + _bsonParse_ifThen_##Then; \ + } else { \ + _bsonDSLDebug ("[else nothing]"); \ + } + +#define _bsonParseOperation_append \ + _bsonParseOperationDeferred_append _bsonDSL_nothing () +#define _bsonParseOperationDeferred_append(Doc, ...) \ + _bsonDSL_begin ("append to [%s] : %s", \ + _bsonDSL_str (Doc), \ + _bsonDSL_strElide (30, __VA_ARGS__)); \ + _bsonBuildAppend (Doc, __VA_ARGS__); \ + if (bsonBuildError) { \ + bsonParseError = bsonBuildError; \ + } \ + _bsonDSL_end + +#define _bsonVisit_applyOps _bsonVisit_applyOpsDeferred _bsonDSL_nothing () +#define _bsonVisit_applyOpsDeferred(...) \ + do { \ + _bsonDSL_mapMacro (_bsonVisit_applyOp, ~, __VA_ARGS__); \ + } while (0); + +#define bsonBuildArray(BSON, ...) \ + _bsonDSL_begin ("bsonBuildArray(%s, %s)", \ + _bsonDSL_str (BSON), \ + _bsonDSL_strElide (30, __VA_ARGS__)); \ + _bsonDSL_eval (_bsonBuildArray (BSON, __VA_ARGS__)); \ + _bsonDSL_end + +#define _bsonBuildArray(BSON, ...) \ + do { \ + _bsonDSL_disableWarnings (); \ + struct _bsonBuildContext_t _bbCtx = { \ + .doc = &(BSON), \ + .parent = _bsonBuildContextThreadLocalPtr, \ + .index = 0, \ + }; \ + _bsonBuildContextThreadLocalPtr = &_bbCtx; \ + _bsonBuildArrayWithCurrentContext (__VA_ARGS__); \ + _bsonBuildContextThreadLocalPtr = _bbCtx.parent; \ + _bsonDSL_restoreWarnings (); \ + } while (0) + +/** + * @brief Build a BSON document by appending to an existing bson_t document + * + * @param Pointer The document upon which to append + * @param ... The Document elements to append to the document + */ +#define bsonBuildAppend(BSON, ...) \ + _bsonDSL_eval (_bsonBuildAppend (BSON, __VA_ARGS__)) +#define _bsonBuildAppend(BSON, ...) \ + _bsonDSL_begin ("Appending to document '%s'", _bsonDSL_str (BSON)); \ + _bsonDSL_disableWarnings (); \ + /* Save the dsl context */ \ + struct _bsonBuildContext_t _bbCtx = { \ + .doc = &(BSON), \ + .parent = _bsonBuildContextThreadLocalPtr, \ + }; \ + /* Reset the context */ \ + _bsonBuildContextThreadLocalPtr = &_bbCtx; \ + bsonBuildError = NULL; \ + _bsonBuildAppendWithCurrentContext (__VA_ARGS__); \ + /* Restore the dsl context */ \ + _bsonBuildContextThreadLocalPtr = _bbCtx.parent; \ + _bsonDSL_restoreWarnings (); \ + _bsonDSL_end + +/** + * @brief Build a new BSON document and assign the value into the given + * pointer. + */ +#define bsonBuild(BSON, ...) \ + _bsonDSL_begin ("Build a new document for '%s'", _bsonDSL_str (BSON)); \ + bson_t *_bbDest = &(BSON); \ + bson_init (_bbDest); \ + bsonBuildAppend (*_bbDest, __VA_ARGS__); \ + _bsonDSL_end + +/** + * @brief Declare a variable and build it with the BSON DSL @see bsonBuild + */ +#define bsonBuildDecl(Variable, ...) \ + bson_t Variable = BSON_INITIALIZER; \ + bsonBuild (Variable, __VA_ARGS__) + + +struct _bsonBuildContext_t { + /// The document that is being built + bson_t *doc; + /// The key that is pending an append + const char *key; + /// The length of the string given in 'key' + int key_len; + /// The index of the array being built (if applicable) + size_t index; + /// A buffer for formatting key strings + char index_key_str[16]; + /// The parent context (if building a sub-document) + struct _bsonBuildContext_t *parent; +}; + +/// A pointer to the current thread's bsonBuild context +_bson_thread_local _bson_comdat struct _bsonBuildContext_t + *_bsonBuildContextThreadLocalPtr = NULL; + +struct _bsonVisitContext_t { + const bson_t *doc; + bson_iter_t iter; + const struct _bsonVisitContext_t *parent; + size_t index; +}; + +/// A pointer to the current thread's bsonVisit/bsonParse context +_bson_thread_local _bson_comdat struct _bsonVisitContext_t const + *_bsonVisitContextThreadLocalPtr = NULL; + +/** + * @brief The most recent error from a bsonBuild() DSL command. + * + * If NULL, no error occurred. Users can assign a value to this string to + * indicate failure. + */ +_bson_thread_local _bson_comdat const char *bsonBuildError = NULL; + +/** + * @brief The most recent error from a buildVisit() or bsonParse() DSL command. + * + * If NULL, no error occurred. Users can assign a value to this string to + * indicate an error. + * + * If this string becomes non-NULL, the current bsonVisit()/bsonParse() will + * halt and return. + * + * Upon entering a new bsonVisit()/bsonParse(), this will be reset to NULL. + */ +_bson_thread_local _bson_comdat const char *bsonParseError = NULL; + +#define _bsonDSLDebug(...) \ + _bson_dsl_debug (BSON_DSL_DEBUG, __FILE__, __LINE__, BSON_FUNC, __VA_ARGS__) + + +static BSON_INLINE bool +_bson_dsl_test_strequal (const char *string, bool case_sensitive) +{ + bson_iter_t it = bsonVisitIter; + if (bson_iter_type (&it) == BSON_TYPE_UTF8) { + uint32_t len; + const char *s = bson_iter_utf8 (&it, &len); + if (len != (uint32_t) strlen (string)) { + return false; + } + if (case_sensitive) { + return memcmp (string, s, len) == 0; + } else { + return bson_strcasecmp (string, s) == 0; + } + } + return false; +} + +static BSON_INLINE bool +_bson_dsl_key_is_anyof (const char *key, + const size_t keylen, + int case_sensitive, + ...) +{ + va_list va; + va_start (va, case_sensitive); + const char *str; + while ((str = va_arg (va, const char *))) { + size_t str_len = strlen (str); + if (str_len != keylen) { + continue; + } + if (case_sensitive) { + if (memcmp (str, key, str_len) == 0) { + va_end (va); + return true; + } + } else { + if (bson_strcasecmp (str, key) == 0) { + va_end (va); + return true; + } + } + } + va_end (va); + return false; +} + +static BSON_INLINE void +_bson_dsl_iter_as_doc (bson_t *into, const bson_iter_t *it) +{ + uint32_t len = 0; + const uint8_t *dataptr = NULL; + if (BSON_ITER_HOLDS_ARRAY (it)) { + bson_iter_array (it, &len, &dataptr); + } else if (BSON_ITER_HOLDS_DOCUMENT (it)) { + bson_iter_document (it, &len, &dataptr); + } + if (dataptr) { + bson_init_static (into, dataptr, len); + } +} + +static BSON_INLINE bool +_bson_dsl_is_empty_bson (const bson_iter_t *it) +{ + bson_t d = BSON_INITIALIZER; + _bson_dsl_iter_as_doc (&d, it); + return d.len == 5; // Empty documents/arrays have byte-size of five +} + +static BSON_INLINE bool +_bson_dsl_iter_is_last_element (const bson_iter_t *it) +{ + bson_iter_t dup = *it; + return !bson_iter_next (&dup) && dup.err_off == 0; +} + +_bson_thread_local _bson_comdat int _bson_dsl_indent = 0; + +static BSON_INLINE void BSON_GNUC_PRINTF (5, 6) + _bson_dsl_debug (bool do_debug, + const char *file, + int line, + const char *func, + const char *string, + ...) +{ + if (do_debug) { + fprintf (stderr, "%s:%d: [%s] bson_dsl: ", file, line, func); + for (int i = 0; i < _bson_dsl_indent; ++i) { + fputs (" ", stderr); + } + va_list va; + va_start (va, string); + vfprintf (stderr, string, va); + va_end (va); + fputc ('\n', stderr); + fflush (stderr); + } +} + +static BSON_INLINE char *BSON_GNUC_PRINTF (2, 3) + _bson_dsl_errorf (char **const into, const char *const fmt, ...) +{ + if (*into) { + bson_free (*into); + *into = NULL; + } + va_list args; + va_start (args, fmt); + *into = bson_strdupv_printf (fmt, args); + va_end (args); + return *into; +} + +static BSON_INLINE void +_bson_dsl_dupPath (char **into) +{ + if (*into) { + bson_free (*into); + *into = NULL; + } + char *acc = bson_strdup (""); + for (const struct _bsonVisitContext_t *ctx = &bsonVisitContext; ctx; + ctx = ctx->parent) { + char *prev = acc; + if (ctx->parent && BSON_ITER_HOLDS_ARRAY (&ctx->parent->iter)) { + // We're an array element + acc = bson_strdup_printf ("[%d]%s", (int)ctx->index, prev); + } else { + // We're a document element + acc = bson_strdup_printf (".%s%s", bson_iter_key (&ctx->iter), prev); + } + bson_free (prev); + } + *into = bson_strdup_printf ("$%s", acc); + bson_free (acc); +} + +static BSON_INLINE const char * +_bsonVisitIterAs_cstr (void) +{ + return bson_iter_utf8 (&bsonVisitIter, NULL); +} + +static BSON_INLINE int32_t +_bsonVisitIterAs_int32 (void) +{ + return bson_iter_int32 (&bsonVisitIter); +} + +static BSON_INLINE bool +_bsonVisitIterAs_bool (void) +{ + return bson_iter_as_bool (&bsonVisitIter); +} + +static BSON_INLINE bool +_bsonVisitIterAs__Bool (void) +{ + return _bsonVisitIterAs_bool (); +} + +#define bsonAs(Type) _bsonDSL_paste (_bsonVisitIterAs_, Type) () + +/// Convert the given argument into a string without inhibitting macro expansion +#define _bsonDSL_str(...) _bsonDSL_str_1 (__VA_ARGS__) +// Empty quotes "" are to ensure a string appears. Old MSVC has a bug +// where empty #__VA_ARGS__ just vanishes. +#define _bsonDSL_str_1(...) "" #__VA_ARGS__ + +#define _bsonDSL_strElide(MaxLen, ...) \ + (strlen (_bsonDSL_str (__VA_ARGS__)) > (MaxLen) \ + ? "[...]" \ + : _bsonDSL_str (__VA_ARGS__)) + +/// Paste two tokens: +#define _bsonDSL_paste(a, ...) _bsonDSL_paste_impl (a, __VA_ARGS__) +#define _bsonDSL_paste_impl(a, ...) a##__VA_ARGS__ + +/// Paste three tokens: +#define _bsonDSL_paste3(a, b, c) _bsonDSL_paste (a, _bsonDSL_paste (b, c)) +/// Paste four tokens: +#define _bsonDSL_paste4(a, b, c, d) \ + _bsonDSL_paste (a, _bsonDSL_paste3 (b, c, d)) + +// clang-format off + +/// Now we need a MAP() macro. This idiom is common, but fairly opaque. Below is +/// some crazy preprocessor trickery to implement it. Fortunately, once we have +/// MAP(), the remainder of this file is straightforward. This implementation +/// isn't the simplest one possible, but is one that supports the old +/// non-compliant MSVC preprocessor. + +/* Expands to nothing. Used to defer a function-like macro and to ignore arguments */ +#define _bsonDSL_nothing(...) + +/// Expand to the 64th argument. See below for why this is useful. +#define _bsonDSL_pick64th(\ + _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, \ + _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, \ + _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, \ + _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, \ + _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, \ + _61, _62, _63, ...) \ + _63 + +/** + * @brief Expands to 1 if the given arguments contain any top-level commas, zero otherwise. + * + * There is an expansion of __VA_ARGS__, followed by 62 '1' arguments, followed + * by single '0'. If __VA_ARGS__ contains no commas, pick64th() will return the + * single zero. If __VA_ARGS__ contains any top-level commas, the series of ones + * will shift to the right and pick64th will return one of those ones. (This only + * works __VA_ARGS__ contains fewer than 62 commas, which is a somewhat reasonable + * limit.) The _bsonDSL_nothing() is a workaround for MSVC's bad preprocessor that + * expands __VA_ARGS__ incorrectly. + * + * If we have __VA_OPT__, this can be a lot simpler. + */ +#define _bsonDSL_hasComma(...) \ + _bsonDSL_pick64th \ + _bsonDSL_nothing() (__VA_ARGS__, \ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, ~) + +/** + * Expands to a single comma if "invoked" as a function-like macro. + * (This will make sense, I promise.) + */ +#define _bsonDSL_commaIfRHSHasParens(...) , + +/** + * @brief Expand to 1 if given no arguments, otherwise 0. + * + * This could be done much more simply using __VA_OPT__, but we need to work on + * older compilers. + */ +#define _bsonDSL_isEmpty(...) \ + _bsonDSL_isEmpty_1(\ + /* Expands to '1' if __VA_ARGS__ contains any top-level commas */ \ + _bsonDSL_hasComma(__VA_ARGS__), \ + /* Expands to '1' if __VA_ARGS__ begins with a parenthesis, because \ + * that will cause an "invocation" of _bsonDSL_commaIfRHSHasParens, \ + * which immediately expands to a single comma. */ \ + _bsonDSL_hasComma(_bsonDSL_commaIfRHSHasParens __VA_ARGS__), \ + /* Expands to '1' if __VA_ARGS__ expands to a function-like macro name \ + * that then expands to anything containing a top-level comma */ \ + _bsonDSL_hasComma(__VA_ARGS__ ()), \ + /* Expands to '1' if __VA_ARGS__ expands to nothing. */ \ + _bsonDSL_hasComma(_bsonDSL_commaIfRHSHasParens __VA_ARGS__ ())) + +/** + * A helper for isEmpty(): If given (0, 0, 0, 1), expands as: + * - first: _bsonDSL_hasComma(_bsonDSL_isEmpty_CASE_0001) + * - then: _bsonDSL_hasComma(,) + * - then: 1 + * Given any other aruments: + * - first: _bsonDSL_hasComma(_bsonDSL_isEmpty_CASE_) + * - then: 0 + */ +#define _bsonDSL_isEmpty_1(_1, _2, _3, _4) \ + _bsonDSL_hasComma(_bsonDSL_paste(_bsonDSL_isEmpty_CASE_, _bsonDSL_paste4(_1, _2, _3, _4))) +#define _bsonDSL_isEmpty_CASE_0001 , + +/** + * @brief Expand to the first argument if `Cond` is 1, the second argument if `Cond` is 0 + */ +#define _bsonDSL_ifElse(Cond, IfTrue, IfFalse) \ + /* Suppress expansion of the two branches by using the '#' operator */ \ + _bsonDSL_nothing(#IfTrue, #IfFalse) \ + /* Concat the cond 1/0 with a prefix macro: */ \ + _bsonDSL_paste(_bsonDSL_ifElse_PICK_, Cond)(IfTrue, IfFalse) + +#define _bsonDSL_ifElse_PICK_1(IfTrue, IfFalse) \ + /* Expand the first operand, throw away the second */ \ + IfTrue _bsonDSL_nothing(#IfFalse) +#define _bsonDSL_ifElse_PICK_0(IfTrue, IfFalse) \ + /* Expand to the second operand, throw away the first */ \ + IfFalse _bsonDSL_nothing(#IfTrue) + +#ifdef _MSC_VER +// MSVC's "traditional" preprocessor requires many more expansion passes, +// but GNU and Clang are very slow when evaluating hugely nested expansions +// and generate massive macro expansion backtraces. +#define _bsonDSL_eval_1(...) __VA_ARGS__ +#define _bsonDSL_eval_2(...) _bsonDSL_eval_1(_bsonDSL_eval_1(_bsonDSL_eval_1(_bsonDSL_eval_1(_bsonDSL_eval_1(__VA_ARGS__))))) +#define _bsonDSL_eval_4(...) _bsonDSL_eval_2(_bsonDSL_eval_2(_bsonDSL_eval_2(_bsonDSL_eval_2(_bsonDSL_eval_2(__VA_ARGS__))))) +#define _bsonDSL_eval_8(...) _bsonDSL_eval_4(_bsonDSL_eval_4(_bsonDSL_eval_4(_bsonDSL_eval_4(_bsonDSL_eval_4(__VA_ARGS__))))) +#define _bsonDSL_eval_16(...) _bsonDSL_eval_8(_bsonDSL_eval_8(_bsonDSL_eval_8(_bsonDSL_eval_8(_bsonDSL_eval_8(__VA_ARGS__))))) +#define _bsonDSL_eval(...) _bsonDSL_eval_16(_bsonDSL_eval_16(_bsonDSL_eval_16(_bsonDSL_eval_16(_bsonDSL_eval_16(__VA_ARGS__))))) +#else +// Each level of "eval" applies double the expansions of the previous level. +#define _bsonDSL_eval_1(...) __VA_ARGS__ +#define _bsonDSL_eval_2(...) _bsonDSL_eval_1(_bsonDSL_eval_1(__VA_ARGS__)) +#define _bsonDSL_eval_4(...) _bsonDSL_eval_2(_bsonDSL_eval_2(__VA_ARGS__)) +#define _bsonDSL_eval_8(...) _bsonDSL_eval_4(_bsonDSL_eval_4(__VA_ARGS__)) +#define _bsonDSL_eval_16(...) _bsonDSL_eval_8(_bsonDSL_eval_8(__VA_ARGS__)) +#define _bsonDSL_eval_32(...) _bsonDSL_eval_16(_bsonDSL_eval_16(__VA_ARGS__)) +#define _bsonDSL_eval(...) _bsonDSL_eval_32(__VA_ARGS__) +#endif + +/** + * Finally, the Map() macro that allows us to do the magic, which we've been + * building up to all along. + * + * The dance with mapMacro_first, mapMacro_final, and _bsonDSL_nothing + * conditional on argument count is to prevent warnings from pre-C99 about + * passing no arguments to the '...' parameters. Yet again, if we had C99 and + * __VA_OPT__ this would be simpler. + */ +#define _bsonDSL_mapMacro(Action, Constant, ...) \ + /* Pick our first action based on the content of '...': */ \ + _bsonDSL_ifElse( \ + /* If given no arguments: */\ + _bsonDSL_isEmpty(__VA_ARGS__), \ + /* expand to _bsonDSL_nothing */ \ + _bsonDSL_nothing, \ + /* Otherwise, expand to mapMacro_first: */ \ + _bsonDSL_mapMacro_first) \ + /* Now "invoke" the chosen macro: */ \ + _bsonDSL_nothing() (Action, Constant, __VA_ARGS__) + +#define _bsonDSL_mapMacro_first(Action, Constant, ...) \ + /* Select our next step based on whether we have one or more arguments: */ \ + _bsonDSL_ifElse( \ + /* If '...' contains more than one argument (has a top-level comma): */ \ + _bsonDSL_hasComma(__VA_ARGS__), \ + /* Begin the mapMacro loop with mapMacro_A: */ \ + _bsonDSL_mapMacro_A, \ + /* Otherwise skip to the final step of the loop: */ \ + _bsonDSL_mapMacro_final) \ + /* Invoke the chosen macro, setting the counter to zero: */ \ + _bsonDSL_nothing() (Action, Constant, 0, __VA_ARGS__) + +/// Handle the last expansion in a mapMacro sequence. +#define _bsonDSL_mapMacro_final(Action, Constant, Counter, FinalElement) \ + Action(FinalElement, Constant, Counter) + +/** + * mapMacro_A and mapMacro_B are identical and just invoke each other. + */ +#define _bsonDSL_mapMacro_A(Action, Constant, Counter, Head, ...) \ + /* First evaluate the action once: */ \ + Action(Head, Constant, Counter) \ + /* Pick our next step: */ \ + _bsonDSL_ifElse( \ + /* If '...' contains more than one argument (has a top-level comma): */ \ + _bsonDSL_hasComma(__VA_ARGS__), \ + /* Jump to the other mapMacro: */ \ + _bsonDSL_mapMacro_B, \ + /* Otherwise go to mapMacro_final */ \ + _bsonDSL_mapMacro_final) \ + /* Invoke the next step of the map: */ \ + _bsonDSL_nothing() (Action, Constant, Counter + 1, __VA_ARGS__) + +#define _bsonDSL_mapMacro_B(Action, Constant, Counter, Head, ...) \ + Action(Head, Constant, Counter) \ + _bsonDSL_ifElse(_bsonDSL_hasComma(__VA_ARGS__), _bsonDSL_mapMacro_A, _bsonDSL_mapMacro_final) \ + _bsonDSL_nothing() (Action, Constant, Counter + 1, __VA_ARGS__) + +// clang-format on + + +#endif // BSON_BSON_DSL_H_INCLUDED diff --git a/src/bson/bson-endian.h b/src/bson/bson-endian.h index b597f57..1acb547 100644 --- a/src/bson/bson-endian.h +++ b/src/bson/bson-endian.h @@ -133,7 +133,7 @@ BSON_BEGIN_DECLS static BSON_INLINE uint16_t __bson_uint16_swap_slow (uint16_t v) /* IN */ { - return ((v & 0x00FF) << 8) | ((v & 0xFF00) >> 8); + return (uint16_t) ((v & 0x00FF) << 8) | (uint16_t) ((v & 0xFF00) >> 8); } diff --git a/src/bson/bson-iter.c b/src/bson/bson-iter.c index c9e839e..00aa5c5 100644 --- a/src/bson/bson-iter.c +++ b/src/bson/bson-iter.c @@ -110,8 +110,13 @@ bson_iter_init_from_data (bson_iter_t *iter, /* OUT */ return false; } + if (BSON_UNLIKELY (!bson_in_range_unsigned (uint32_t, length))) { + memset (iter, 0, sizeof *iter); + return false; + } + iter->raw = (uint8_t *) data; - iter->len = length; + iter->len = (uint32_t) length; iter->off = 0; iter->type = 0; iter->key = 0; @@ -1894,8 +1899,8 @@ bson_iter_visit_all (bson_iter_t *iter, /* INOUT */ const bson_visitor_t *visitor, /* IN */ void *data) /* IN */ { - uint32_t bson_type; - const char *key; + uint32_t bson_type = 0; + const char *key = NULL; bool unsupported; BSON_ASSERT (iter); diff --git a/src/bson/bson-iter.h b/src/bson/bson-iter.h index 92e1968..f556154 100644 --- a/src/bson/bson-iter.h +++ b/src/bson/bson-iter.h @@ -109,11 +109,15 @@ bson_iter_value (bson_iter_t *iter); static BSON_INLINE uint32_t bson_iter_utf8_len_unsafe (const bson_iter_t *iter) { - int32_t val; + uint32_t raw; + memcpy (&raw, iter->raw + iter->d1, sizeof (raw)); - memcpy (&val, iter->raw + iter->d1, sizeof (val)); - val = BSON_UINT32_FROM_LE (val); - return BSON_MAX (0, val - 1); + const uint32_t native = BSON_UINT32_FROM_LE (raw); + + int32_t len; + memcpy (&len, &native, sizeof (len)); + + return len <= 0 ? 0u : (uint32_t) (len - 1); } @@ -242,10 +246,14 @@ bson_iter_int32 (const bson_iter_t *iter); static BSON_INLINE int32_t bson_iter_int32_unsafe (const bson_iter_t *iter) { - int32_t val; + uint32_t raw; + memcpy (&raw, iter->raw + iter->d1, sizeof (raw)); - memcpy (&val, iter->raw + iter->d1, sizeof (val)); - return BSON_UINT32_FROM_LE (val); + const uint32_t native = BSON_UINT32_FROM_LE (raw); + + int32_t res; + memcpy (&res, &native, sizeof (res)); + return res; } @@ -268,10 +276,14 @@ bson_iter_as_int64 (const bson_iter_t *iter); static BSON_INLINE int64_t bson_iter_int64_unsafe (const bson_iter_t *iter) { - int64_t val; + uint64_t raw; + memcpy (&raw, iter->raw + iter->d1, sizeof (raw)); - memcpy (&val, iter->raw + iter->d1, sizeof (val)); - return BSON_UINT64_FROM_LE (val); + const uint64_t native = BSON_UINT64_FROM_LE (raw); + + int64_t res; + memcpy (&res, &native, sizeof (res)); + return res; } @@ -407,7 +419,7 @@ bson_iter_time_t (const bson_iter_t *iter); static BSON_INLINE time_t bson_iter_time_t_unsafe (const bson_iter_t *iter) { - return (time_t) (bson_iter_int64_unsafe (iter) / 1000UL); + return (time_t) (bson_iter_int64_unsafe (iter) / 1000); } @@ -511,7 +523,8 @@ bson_iter_overwrite_double (bson_iter_t *iter, double value); BSON_EXPORT (void) -bson_iter_overwrite_decimal128 (bson_iter_t *iter, const bson_decimal128_t *value); +bson_iter_overwrite_decimal128 (bson_iter_t *iter, + const bson_decimal128_t *value); BSON_EXPORT (void) diff --git a/src/bson/bson-json-private.h b/src/bson/bson-json-private.h index 9f51dcb..3db1f74 100644 --- a/src/bson/bson-json-private.h +++ b/src/bson/bson-json-private.h @@ -23,6 +23,7 @@ struct _bson_json_opts_t { bson_json_mode_t mode; int32_t max_len; + bool is_outermost_array; }; diff --git a/src/bson/bson-json.c b/src/bson/bson-json.c index 7e838f2..a74ec37 100644 --- a/src/bson/bson-json.c +++ b/src/bson/bson-json.c @@ -370,8 +370,9 @@ _noop (void) read_state_names[bson->read_state]); \ return; \ } -#define HANDLE_OPTION(_key, _type, _state) \ - (len == strlen (_key) && strncmp ((const char *) val, (_key), len) == 0) \ +#define HANDLE_OPTION(_selection_statement, _key, _type, _state) \ + _selection_statement (len == strlen (_key) && \ + strncmp ((const char *) val, (_key), len) == 0) \ { \ if (bson->bson_type && bson->bson_type != (_type)) { \ _bson_json_read_set_error (reader, \ @@ -387,15 +388,17 @@ _noop (void) } - bson_json_opts_t * bson_json_opts_new (bson_json_mode_t mode, int32_t max_len) { bson_json_opts_t *opts; opts = (bson_json_opts_t *) bson_malloc (sizeof *opts); - opts->mode = mode; - opts->max_len = max_len; + *opts = (bson_json_opts_t){ + .mode = mode, + .max_len = max_len, + .is_outermost_array = false, + }; return opts; } @@ -628,7 +631,13 @@ _bson_json_read_integer (bson_json_reader_t *reader, uint64_t val, int64_t sign) bson_append_int32 ( STACK_BSON_CHILD, key, (int) len, (int) (val * sign)); } else if (sign == -1) { +#if defined(_WIN32) && !defined(__MINGW32__) + // Unary negation of unsigned integer is deliberate. +#pragma warning(suppress : 4146) + bson_append_int64 (STACK_BSON_CHILD, key, (int) len, (int64_t) -val); +#else bson_append_int64 (STACK_BSON_CHILD, key, (int) len, (int64_t) -val); +#endif // defined(_WIN32) && !defined(__MINGW32__) } else { bson_append_int64 (STACK_BSON_CHILD, key, (int) len, (int64_t) val); } @@ -735,33 +744,19 @@ _bson_json_parse_double (bson_json_reader_t *reader, *d = strtod (val, NULL); #ifdef _MSC_VER + const double pos_inf = INFINITY; + const double neg_inf = -pos_inf; + /* Microsoft's strtod parses "NaN", "Infinity", "-Infinity" as 0 */ if (*d == 0.0) { if (!_strnicmp (val, "nan", vlen)) { -#ifdef NAN *d = NAN; -#else - /* Visual Studio 2010 doesn't define NAN or INFINITY - * https://msdn.microsoft.com/en-us/library/w22adx1s(v=vs.100).aspx */ - unsigned long nan[2] = {0xffffffff, 0x7fffffff}; - *d = *(double *) nan; -#endif return true; } else if (!_strnicmp (val, "infinity", vlen)) { -#ifdef INFINITY - *d = INFINITY; -#else - unsigned long inf[2] = {0x00000000, 0x7ff00000}; - *d = *(double *) inf; -#endif + *d = pos_inf; return true; } else if (!_strnicmp (val, "-infinity", vlen)) { -#ifdef INFINITY - *d = -INFINITY; -#else - unsigned long inf[2] = {0x00000000, 0xfff00000}; - *d = *(double *) inf; -#endif + *d = neg_inf; return true; } } @@ -831,7 +826,7 @@ static bool _unhexlify_uuid (const char *uuid, uint8_t *out, size_t max) { unsigned int byte; - int x = 0; + size_t x = 0; int i = 0; BSON_ASSERT (strlen (uuid) == 32); @@ -866,7 +861,7 @@ _bson_json_parse_binary_elem (bson_json_reader_t *reader, if (bs == BSON_JSON_LF_BINARY) { data->binary.has_binary = true; - binary_len = COMMON_PREFIX (bson_b64_pton (val_w_null, NULL, 0)); + binary_len = mcommon_b64_pton (val_w_null, NULL, 0); if (binary_len < 0) { _bson_json_read_set_error ( reader, @@ -875,9 +870,9 @@ _bson_json_parse_binary_elem (bson_json_reader_t *reader, } _bson_json_buf_ensure (&bson->bson_type_buf[0], (size_t) binary_len + 1); - if (COMMON_PREFIX (bson_b64_pton (val_w_null, - bson->bson_type_buf[0].buf, - (size_t) binary_len + 1) < 0)) { + if (mcommon_b64_pton (val_w_null, + bson->bson_type_buf[0].buf, + (size_t) binary_len + 1) < 0) { _bson_json_read_set_error ( reader, "Invalid input string \"%s\", looking for base64-encoded binary", @@ -1174,18 +1169,60 @@ _bson_json_read_start_map (bson_json_reader_t *reader) /* IN */ BASIC_CB_PREAMBLE; if (bson->read_state == BSON_JSON_IN_BSON_TYPE) { - if (bson->bson_state == BSON_JSON_LF_DATE) { + switch (bson->bson_state) { + case BSON_JSON_LF_DATE: bson->read_state = BSON_JSON_IN_BSON_TYPE_DATE_NUMBERLONG; - } else if (bson->bson_state == BSON_JSON_LF_BINARY) { + break; + case BSON_JSON_LF_BINARY: bson->read_state = BSON_JSON_IN_BSON_TYPE_BINARY_VALUES; - } else if (bson->bson_state == BSON_JSON_LF_TYPE) { + break; + case BSON_JSON_LF_TYPE: /* special case, we started parsing {$type: {$numberInt: "2"}} and we * expected a legacy Binary format. now we see the second "{", so * backtrack and parse $type query operator. */ bson->read_state = BSON_JSON_IN_START_MAP; + BSON_ASSERT (bson_in_range_unsigned (int, len)); STACK_PUSH_DOC (bson_append_document_begin ( - STACK_BSON_PARENT, key, len, STACK_BSON_CHILD)); + STACK_BSON_PARENT, key, (int) len, STACK_BSON_CHILD)); _bson_json_save_map_key (bson, (const uint8_t *) "$type", 5); + break; + case BSON_JSON_LF_CODE: + case BSON_JSON_LF_DECIMAL128: + case BSON_JSON_LF_DOUBLE: + case BSON_JSON_LF_INT32: + case BSON_JSON_LF_INT64: + case BSON_JSON_LF_MAXKEY: + case BSON_JSON_LF_MINKEY: + case BSON_JSON_LF_OID: + case BSON_JSON_LF_OPTIONS: + case BSON_JSON_LF_REGEX: + /** + * NOTE: A read_state of BSON_JSON_IN_BSON_TYPE is used when "$regex" is + * found, but BSON_JSON_IN_BSON_TYPE_REGEX_STARTMAP is used for + * "$regularExpression", which will instead go to a below 'if else' branch + * instead of this switch statement. They're both called "regex" in their + * respective enumerators, but they behave differently when parsing. + */ + // fallthrough + case BSON_JSON_LF_REGULAR_EXPRESSION_OPTIONS: + case BSON_JSON_LF_REGULAR_EXPRESSION_PATTERN: + case BSON_JSON_LF_SYMBOL: + case BSON_JSON_LF_UNDEFINED: + case BSON_JSON_LF_UUID: + // These special keys do not expect objects as their values. Fail. + _bson_json_read_set_error ( + reader, + "Unexpected nested object value for \"%s\" key", + reader->bson.unescaped.buf); + break; + case BSON_JSON_LF_DBPOINTER: + case BSON_JSON_LF_SCOPE: + case BSON_JSON_LF_TIMESTAMP_I: + case BSON_JSON_LF_TIMESTAMP_T: + default: + // These special LF keys aren't handled with BSON_JSON_IN_BSON_TYPE + BSON_UNREACHABLE ( + "These LF values are handled with a different read_state"); } } else if (bson->read_state == BSON_JSON_IN_BSON_TYPE_TIMESTAMP_STARTMAP) { bson->read_state = BSON_JSON_IN_BSON_TYPE_TIMESTAMP_VALUES; @@ -1329,90 +1366,92 @@ _bson_json_read_map_key (bson_json_reader_t *reader, /* IN */ } if (bson->read_state == BSON_JSON_IN_BSON_TYPE) { - if - HANDLE_OPTION ("$regex", BSON_TYPE_REGEX, BSON_JSON_LF_REGEX) - else if - HANDLE_OPTION ("$options", BSON_TYPE_REGEX, BSON_JSON_LF_OPTIONS) - else if - HANDLE_OPTION ("$oid", BSON_TYPE_OID, BSON_JSON_LF_OID) - else if - HANDLE_OPTION ("$binary", BSON_TYPE_BINARY, BSON_JSON_LF_BINARY) - else if - HANDLE_OPTION ("$type", BSON_TYPE_BINARY, BSON_JSON_LF_TYPE) - else if - HANDLE_OPTION ("$uuid", BSON_TYPE_BINARY, BSON_JSON_LF_UUID) - else if - HANDLE_OPTION ("$date", BSON_TYPE_DATE_TIME, BSON_JSON_LF_DATE) - else if - HANDLE_OPTION ( - "$undefined", BSON_TYPE_UNDEFINED, BSON_JSON_LF_UNDEFINED) - else if - HANDLE_OPTION ("$minKey", BSON_TYPE_MINKEY, BSON_JSON_LF_MINKEY) - else if - HANDLE_OPTION ("$maxKey", BSON_TYPE_MAXKEY, BSON_JSON_LF_MAXKEY) - else if - HANDLE_OPTION ("$numberInt", BSON_TYPE_INT32, BSON_JSON_LF_INT32) - else if - HANDLE_OPTION ("$numberLong", BSON_TYPE_INT64, BSON_JSON_LF_INT64) - else if - HANDLE_OPTION ("$numberDouble", BSON_TYPE_DOUBLE, BSON_JSON_LF_DOUBLE) - else if - HANDLE_OPTION ("$symbol", BSON_TYPE_SYMBOL, BSON_JSON_LF_SYMBOL) - else if - HANDLE_OPTION ( - "$numberDecimal", BSON_TYPE_DECIMAL128, BSON_JSON_LF_DECIMAL128) - else if (!strcmp ("$timestamp", (const char *) val)) { + HANDLE_OPTION (if, "$regex", BSON_TYPE_REGEX, BSON_JSON_LF_REGEX) + HANDLE_OPTION (else if, "$options", BSON_TYPE_REGEX, BSON_JSON_LF_OPTIONS) + HANDLE_OPTION (else if, "$oid", BSON_TYPE_OID, BSON_JSON_LF_OID) + HANDLE_OPTION (else if, "$binary", BSON_TYPE_BINARY, BSON_JSON_LF_BINARY) + HANDLE_OPTION (else if, "$type", BSON_TYPE_BINARY, BSON_JSON_LF_TYPE) + HANDLE_OPTION (else if, "$uuid", BSON_TYPE_BINARY, BSON_JSON_LF_UUID) + HANDLE_OPTION (else if, "$date", BSON_TYPE_DATE_TIME, BSON_JSON_LF_DATE) + HANDLE_OPTION ( + else if, "$undefined", BSON_TYPE_UNDEFINED, BSON_JSON_LF_UNDEFINED) + HANDLE_OPTION (else if, "$minKey", BSON_TYPE_MINKEY, BSON_JSON_LF_MINKEY) + HANDLE_OPTION (else if, "$maxKey", BSON_TYPE_MAXKEY, BSON_JSON_LF_MAXKEY) + HANDLE_OPTION (else if, "$numberInt", BSON_TYPE_INT32, BSON_JSON_LF_INT32) + HANDLE_OPTION ( + else if, "$numberLong", BSON_TYPE_INT64, BSON_JSON_LF_INT64) + HANDLE_OPTION ( + else if, "$numberDouble", BSON_TYPE_DOUBLE, BSON_JSON_LF_DOUBLE) + HANDLE_OPTION (else if, "$symbol", BSON_TYPE_SYMBOL, BSON_JSON_LF_SYMBOL) + HANDLE_OPTION (else if, + "$numberDecimal", + BSON_TYPE_DECIMAL128, + BSON_JSON_LF_DECIMAL128) + else if (!strcmp ("$timestamp", (const char *) val)) + { bson->bson_type = BSON_TYPE_TIMESTAMP; bson->read_state = BSON_JSON_IN_BSON_TYPE_TIMESTAMP_STARTMAP; - } else if (!strcmp ("$regularExpression", (const char *) val)) { + } + else if (!strcmp ("$regularExpression", (const char *) val)) + { bson->bson_type = BSON_TYPE_REGEX; bson->read_state = BSON_JSON_IN_BSON_TYPE_REGEX_STARTMAP; - } else if (!strcmp ("$dbPointer", (const char *) val)) { + } + else if (!strcmp ("$dbPointer", (const char *) val)) + { /* start parsing "key": {"$dbPointer": {...}}, save "key" for later */ _bson_json_buf_set ( &bson->dbpointer_key, bson->key_buf.buf, bson->key_buf.len); bson->bson_type = BSON_TYPE_DBPOINTER; bson->read_state = BSON_JSON_IN_BSON_TYPE_DBPOINTER_STARTMAP; - } else if (!strcmp ("$code", (const char *) val)) { + } + else if (!strcmp ("$code", (const char *) val)) + { _bson_json_read_code_or_scope_key ( bson, false /* is_scope */, val, len); - } else if (!strcmp ("$scope", (const char *) val)) { + } + else if (!strcmp ("$scope", (const char *) val)) + { _bson_json_read_code_or_scope_key ( bson, true /* is_scope */, val, len); - } else { + } + else + { _bson_json_bad_key_in_type (reader, val); } } else if (bson->read_state == BSON_JSON_IN_BSON_TYPE_DATE_NUMBERLONG) { - if - HANDLE_OPTION ("$numberLong", BSON_TYPE_DATE_TIME, BSON_JSON_LF_INT64) - else { + HANDLE_OPTION (if, "$numberLong", BSON_TYPE_DATE_TIME, BSON_JSON_LF_INT64) + else + { _bson_json_bad_key_in_type (reader, val); } } else if (bson->read_state == BSON_JSON_IN_BSON_TYPE_TIMESTAMP_VALUES) { - if - HANDLE_OPTION ("t", BSON_TYPE_TIMESTAMP, BSON_JSON_LF_TIMESTAMP_T) - else if - HANDLE_OPTION ("i", BSON_TYPE_TIMESTAMP, BSON_JSON_LF_TIMESTAMP_I) - else { + HANDLE_OPTION (if, "t", BSON_TYPE_TIMESTAMP, BSON_JSON_LF_TIMESTAMP_T) + HANDLE_OPTION ( + else if, "i", BSON_TYPE_TIMESTAMP, BSON_JSON_LF_TIMESTAMP_I) + else + { _bson_json_bad_key_in_type (reader, val); } } else if (bson->read_state == BSON_JSON_IN_BSON_TYPE_REGEX_VALUES) { - if - HANDLE_OPTION ( - "pattern", BSON_TYPE_REGEX, BSON_JSON_LF_REGULAR_EXPRESSION_PATTERN) - else if - HANDLE_OPTION ( - "options", BSON_TYPE_REGEX, BSON_JSON_LF_REGULAR_EXPRESSION_OPTIONS) - else { + HANDLE_OPTION (if, + "pattern", + BSON_TYPE_REGEX, + BSON_JSON_LF_REGULAR_EXPRESSION_PATTERN) + HANDLE_OPTION (else if, + "options", + BSON_TYPE_REGEX, + BSON_JSON_LF_REGULAR_EXPRESSION_OPTIONS) + else + { _bson_json_bad_key_in_type (reader, val); } } else if (bson->read_state == BSON_JSON_IN_BSON_TYPE_BINARY_VALUES) { - if - HANDLE_OPTION ("base64", BSON_TYPE_BINARY, BSON_JSON_LF_BINARY) - else if - HANDLE_OPTION ("subType", BSON_TYPE_BINARY, BSON_JSON_LF_TYPE) - else { + HANDLE_OPTION (if, "base64", BSON_TYPE_BINARY, BSON_JSON_LF_BINARY) + HANDLE_OPTION (else if, "subType", BSON_TYPE_BINARY, BSON_JSON_LF_TYPE) + else + { _bson_json_bad_key_in_type (reader, val); } } else { @@ -1971,6 +2010,9 @@ _push_callback (jsonsl_t json, { bson_json_reader_t *reader = (bson_json_reader_t *) json->data; + BSON_UNUSED (action); + BSON_UNUSED (buf); + switch (state->type) { case JSONSL_T_STRING: case JSONSL_T_HKEY: @@ -2002,6 +2044,8 @@ _pop_callback (jsonsl_t json, double d; const char *obj_text; + BSON_UNUSED (action); + reader = (bson_json_reader_t *) json->data; reader_bson = &reader->bson; @@ -2069,6 +2113,8 @@ _error_callback (jsonsl_t json, { bson_json_reader_t *reader = (bson_json_reader_t *) json->data; + BSON_UNUSED (state); + if (err == JSONSL_ERROR_CANT_INSERT && *errat == '{') { /* start the next document */ reader->should_reset = true; @@ -2183,8 +2229,11 @@ bson_json_reader_read (bson_json_reader_t *reader, /* IN */ /* accumulate a key or string value */ if (reader->json_text_pos != -1) { - if (reader->json_text_pos < reader->json->pos) { - accum = BSON_MIN (reader->json->pos - reader->json_text_pos, r); + if (bson_cmp_less_su (reader->json_text_pos, reader->json->pos)) { + BSON_ASSERT ( + bson_in_range_unsigned (ssize_t, reader->json->pos)); + accum = BSON_MIN ( + (ssize_t) reader->json->pos - reader->json_text_pos, r); /* if this chunk stopped mid-token, buf_offset is how far into * our current chunk the token begins. */ buf_offset = AT_LEAST_0 (reader->json_text_pos - start_pos); @@ -2219,7 +2268,9 @@ bson_json_reader_new (void *data, /* IN */ bson_json_reader_t *r; bson_json_reader_producer_t *p; - r = bson_malloc0 (sizeof *r); + BSON_UNUSED (allow_multiple); + + r = BSON_ALIGNED_ALLOC0 (bson_json_reader_t); r->json = jsonsl_new (STACK_MAX); r->json->error_callback = _error_callback; r->json->action_callback_PUSH = _push_callback; @@ -2287,6 +2338,14 @@ bson_json_reader_destroy (bson_json_reader_t *reader) /* IN */ } +void +bson_json_opts_set_outermost_array (bson_json_opts_t *opts, + bool is_outermost_array) +{ + opts->is_outermost_array = is_outermost_array; +} + + typedef struct { const uint8_t *data; size_t len; diff --git a/src/bson/bson-json.h b/src/bson/bson-json.h index 20fab57..0e001ee 100644 --- a/src/bson/bson-json.h +++ b/src/bson/bson-json.h @@ -61,7 +61,9 @@ BSON_EXPORT (bson_json_opts_t *) bson_json_opts_new (bson_json_mode_t mode, int32_t max_len); BSON_EXPORT (void) bson_json_opts_destroy (bson_json_opts_t *opts); - +BSON_EXPORT (void) +bson_json_opts_set_outermost_array (bson_json_opts_t *opts, + bool is_outermost_array); typedef ssize_t (*bson_json_reader_cb) (void *handle, uint8_t *buf, diff --git a/src/bson/bson-macros.h b/src/bson/bson-macros.h index 8aa7288..f6caa57 100644 --- a/src/bson/bson-macros.h +++ b/src/bson/bson-macros.h @@ -141,14 +141,25 @@ #define BSON_ABS(a) (((a) < 0) ? ((a) * -1) : (a)) #endif +#if __STDC_VERSION__ >= 201112L +#define BSON_ALIGNOF(expr) _Alignof(expr) +#else +#if defined(_MSC_VER) +#define BSON_ALIGNOF(expr) __alignof(expr) +#else +#define BSON_ALIGNOF(expr) __alignof__(expr) +#endif +#endif // __STDC_VERSION__ >= 201112L + #ifdef _MSC_VER +// __declspec (align (_N)) only permits integer literals as _N. #ifdef _WIN64 #define BSON_ALIGN_OF_PTR 8 #else #define BSON_ALIGN_OF_PTR 4 #endif #else -#define BSON_ALIGN_OF_PTR (sizeof (void *)) +#define BSON_ALIGN_OF_PTR (BSON_ALIGNOF (void *)) #endif #ifdef BSON_EXTRA_ALIGN @@ -178,8 +189,6 @@ #if defined(_MSC_VER) #define BSON_FUNC __FUNCTION__ -#elif defined(__STDC_VERSION__) && __STDC_VERSION__ < 199901L -#define BSON_FUNC __FUNCTION__ #else #define BSON_FUNC __func__ #endif @@ -370,4 +379,15 @@ abort (); \ } while (0) +/** + * @brief Silence warnings for deliberately unused variables or parameters. + * + * @param expr An unused variable or parameter. + * + */ +#define BSON_UNUSED(expr) \ + do { \ + (void) (expr); \ + } while (0) + #endif /* BSON_MACROS_H */ diff --git a/src/bson/bson-md5.c b/src/bson/bson-md5.c index 3637731..71d7a54 100644 --- a/src/bson/bson-md5.c +++ b/src/bson/bson-md5.c @@ -7,18 +7,18 @@ void bson_md5_init (bson_md5_t *pms) { - COMMON_PREFIX (_bson_md5_init (pms)); + mcommon_md5_init (pms); } void bson_md5_append (bson_md5_t *pms, const uint8_t *data, uint32_t nbytes) { - COMMON_PREFIX (_bson_md5_append (pms, data, nbytes)); + mcommon_md5_append (pms, data, nbytes); } void bson_md5_finish (bson_md5_t *pms, uint8_t digest[16]) { - COMMON_PREFIX (_bson_md5_finish (pms, digest)); + mcommon_md5_finish (pms, digest); } diff --git a/src/bson/bson-memory.c b/src/bson/bson-memory.c index 27832f2..ede308b 100644 --- a/src/bson/bson-memory.c +++ b/src/bson/bson-memory.c @@ -25,12 +25,46 @@ #include "bson-memory.h" -static bson_mem_vtable_t gMemVtable = { - malloc, - calloc, - realloc, - free, -}; +// Ensure size of exported structs are stable. +BSON_STATIC_ASSERT2 (bson_mem_vtable_t, + sizeof (bson_mem_vtable_t) == sizeof (void *) * 8u); + + +// For compatibility with C standards prior to C11. +static void * +_aligned_alloc_impl (size_t alignment, size_t num_bytes) +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && \ + !defined(_WIN32) && !defined(__ANDROID__) +{ + return aligned_alloc (alignment, num_bytes); +} +#elif defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112L +{ + void *mem = NULL; + + // Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66425. + BSON_MAYBE_UNUSED int ret = posix_memalign (&mem, alignment, num_bytes); + + return mem; +} +#else +{ + // Fallback to simple malloc even if it does not satisfy alignment + // requirements. Note: Visual C++ _aligned_malloc requires using + // _aligned_free instead of free and modifies errno on failure, both of which + // breaks symmetry with C11 aligned_alloc, so it is deliberately not used. + BSON_UNUSED (alignment); + return malloc (num_bytes); +} +#endif + + +static bson_mem_vtable_t gMemVtable = {.malloc = malloc, + .calloc = calloc, + .realloc = realloc, + .free = free, + .aligned_alloc = _aligned_alloc_impl, + .padding = {0}}; /* @@ -64,7 +98,9 @@ bson_malloc (size_t num_bytes) /* IN */ if (BSON_LIKELY (num_bytes)) { if (BSON_UNLIKELY (!(mem = gMemVtable.malloc (num_bytes)))) { - fprintf (stderr, "Failure to allocate memory in bson_malloc(). errno: %d.\n", errno); + fprintf (stderr, + "Failure to allocate memory in bson_malloc(). errno: %d.\n", + errno); abort (); } } @@ -102,9 +138,96 @@ bson_malloc0 (size_t num_bytes) /* IN */ if (BSON_LIKELY (num_bytes)) { if (BSON_UNLIKELY (!(mem = gMemVtable.calloc (1, num_bytes)))) { - fprintf (stderr, "Failure to allocate memory in bson_malloc0(). errno: %d.\n", errno); + fprintf (stderr, + "Failure to allocate memory in bson_malloc0(). errno: %d.\n", + errno); + abort (); + } + } + + return mem; +} + + +/* + *-------------------------------------------------------------------------- + * + * bson_aligned_malloc -- + * + * Allocates @num_bytes of memory with an alignment of @alignment and + * returns a pointer to it. If malloc failed to allocate the memory, + * abort() is called. + * + * Libbson does not try to handle OOM conditions as it is beyond the + * scope of this library to handle so appropriately. + * + * Parameters: + * @alignment: The alignment of the allocated bytes of memory. + * @num_bytes: The number of bytes to allocate. + * + * Returns: + * A pointer if successful; otherwise abort() is called and this + * function will never return. + * + * Side effects: + * None. + * + *-------------------------------------------------------------------------- + */ + +void * +bson_aligned_alloc (size_t alignment /* IN */, size_t num_bytes /* IN */) +{ + void *mem = NULL; + + if (BSON_LIKELY (num_bytes)) { + if (BSON_UNLIKELY ( + !(mem = gMemVtable.aligned_alloc (alignment, num_bytes)))) { + fprintf (stderr, + "Failure to allocate memory in bson_aligned_alloc()\n"); + abort (); + } + } + + return mem; +} + + +/* + *-------------------------------------------------------------------------- + * + * bson_aligned_alloc0 -- + * + * Like bson_aligned_alloc() except the memory is zeroed after allocation + * for convenience. + * + * Parameters: + * @alignment: The alignment of the allocated bytes of memory. + * @num_bytes: The number of bytes to allocate. + * + * Returns: + * A pointer if successful; otherwise abort() is called and this + * function will never return. + * + * Side effects: + * None. + * + *-------------------------------------------------------------------------- + */ + +void * +bson_aligned_alloc0 (size_t alignment /* IN */, size_t num_bytes /* IN */) +{ + void *mem = NULL; + + if (BSON_LIKELY (num_bytes)) { + if (BSON_UNLIKELY ( + !(mem = gMemVtable.aligned_alloc (alignment, num_bytes)))) { + fprintf (stderr, + "Failure to allocate memory in bson_aligned_alloc0()\n"); abort (); } + memset (mem, 0, num_bytes); } return mem; @@ -150,7 +273,9 @@ bson_realloc (void *mem, /* IN */ mem = gMemVtable.realloc (mem, num_bytes); if (BSON_UNLIKELY (!mem)) { - fprintf (stderr, "Failure to re-allocate memory in bson_realloc(). errno: %d.\n", errno); + fprintf (stderr, + "Failure to re-allocate memory in bson_realloc(). errno: %d.\n", + errno); abort (); } @@ -187,6 +312,8 @@ bson_realloc_ctx (void *mem, /* IN */ size_t num_bytes, /* IN */ void *ctx) /* IN */ { + BSON_UNUSED (ctx); + return bson_realloc (mem, num_bytes); } @@ -253,6 +380,15 @@ bson_zero_free (void *mem, /* IN */ } +static void * +_aligned_alloc_as_malloc (size_t alignment, size_t num_bytes) +{ + BSON_UNUSED (alignment); + + return gMemVtable.malloc (num_bytes); +} + + /* *-------------------------------------------------------------------------- * @@ -287,17 +423,22 @@ bson_mem_set_vtable (const bson_mem_vtable_t *vtable) } gMemVtable = *vtable; + + // Backwards compatibility with code prior to addition of aligned_alloc. + if (!gMemVtable.aligned_alloc) { + gMemVtable.aligned_alloc = _aligned_alloc_as_malloc; + } } void bson_mem_restore_vtable (void) { - bson_mem_vtable_t vtable = { - malloc, - calloc, - realloc, - free, - }; + bson_mem_vtable_t vtable = {.malloc = malloc, + .calloc = calloc, + .realloc = realloc, + .free = free, + .aligned_alloc = _aligned_alloc_impl, + .padding = {0}}; bson_mem_set_vtable (&vtable); } diff --git a/src/bson/bson-memory.h b/src/bson/bson-memory.h index c26d218..a0f8c23 100644 --- a/src/bson/bson-memory.h +++ b/src/bson/bson-memory.h @@ -36,7 +36,8 @@ typedef struct _bson_mem_vtable_t { void *(*calloc) (size_t n_members, size_t num_bytes); void *(*realloc) (void *mem, size_t num_bytes); void (*free) (void *mem); - void *padding[4]; + void *(*aligned_alloc) (size_t alignment, size_t num_bytes); + void *padding[3]; } bson_mem_vtable_t; @@ -49,6 +50,10 @@ bson_malloc (size_t num_bytes); BSON_EXPORT (void *) bson_malloc0 (size_t num_bytes); BSON_EXPORT (void *) +bson_aligned_alloc (size_t alignment, size_t num_bytes); +BSON_EXPORT (void *) +bson_aligned_alloc0 (size_t alignment, size_t num_bytes); +BSON_EXPORT (void *) bson_realloc (void *mem, size_t num_bytes); BSON_EXPORT (void *) bson_realloc_ctx (void *mem, size_t num_bytes, void *ctx); @@ -58,6 +63,12 @@ BSON_EXPORT (void) bson_zero_free (void *mem, size_t size); +#define BSON_ALIGNED_ALLOC(T) \ + ((T *) (bson_aligned_alloc (BSON_ALIGNOF (T), sizeof (T)))) +#define BSON_ALIGNED_ALLOC0(T) \ + ((T *) (bson_aligned_alloc0 (BSON_ALIGNOF (T), sizeof (T)))) + + BSON_END_DECLS diff --git a/src/bson/bson-oid.c b/src/bson/bson-oid.c index 29b19c3..3747713 100644 --- a/src/bson/bson-oid.c +++ b/src/bson/bson-oid.c @@ -32,7 +32,7 @@ * to hex formatted ASCII. Performing two characters at a time roughly * reduces the number of operations by one-half. */ -static const uint16_t gHexCharPairs[] = { +BSON_MAYBE_UNUSED static const uint16_t gHexCharPairs[] = { #if BSON_BYTE_ORDER == BSON_BIG_ENDIAN 12336, 12337, 12338, 12339, 12340, 12341, 12342, 12343, 12344, 12345, 12385, 12386, 12387, 12388, 12389, 12390, 12592, 12593, 12594, 12595, 12596, 12597, @@ -98,9 +98,8 @@ bson_oid_init_sequence (bson_oid_t *oid, /* OUT */ } now = BSON_UINT32_TO_BE (now); - memcpy (&oid->bytes[0], &now, sizeof (now)); - context->oid_set_seq64 (context, oid); + _bson_context_set_oid_seq64 (context, oid); } @@ -118,9 +117,8 @@ bson_oid_init (bson_oid_t *oid, /* OUT */ now = BSON_UINT32_TO_BE (now); memcpy (&oid->bytes[0], &now, sizeof (now)); - _bson_context_set_oid_rand (context, oid); - context->oid_set_seq32 (context, oid); + _bson_context_set_oid_seq32 (context, oid); } diff --git a/src/bson/bson-oid.h b/src/bson/bson-oid.h index 25f4bd6..2a8853e 100644 --- a/src/bson/bson-oid.h +++ b/src/bson/bson-oid.h @@ -51,8 +51,8 @@ bson_oid_init_from_data (bson_oid_t *oid, const uint8_t *data); BSON_EXPORT (void) bson_oid_init_from_string (bson_oid_t *oid, const char *str); BSON_EXPORT (void) -bson_oid_init_sequence (bson_oid_t *oid, - bson_context_t *context) BSON_GNUC_DEPRECATED; +bson_oid_init_sequence (bson_oid_t *oid, bson_context_t *context) + BSON_GNUC_DEPRECATED_FOR (bson_oid_init); BSON_EXPORT (void) bson_oid_to_string (const bson_oid_t *oid, char str[25]); @@ -214,8 +214,8 @@ bson_oid_init_from_string_unsafe (bson_oid_t *oid, const char *str) int i; for (i = 0; i < 12; i++) { - oid->bytes[i] = ((bson_oid_parse_hex_char (str[2 * i]) << 4) | - (bson_oid_parse_hex_char (str[2 * i + 1]))); + oid->bytes[i] = (uint8_t) ((bson_oid_parse_hex_char (str[2 * i]) << 4) | + (bson_oid_parse_hex_char (str[2 * i + 1]))); } } diff --git a/src/bson/bson-reader.c b/src/bson/bson-reader.c index e20a8de..e0a5c97 100644 --- a/src/bson/bson-reader.c +++ b/src/bson/bson-reader.c @@ -168,7 +168,7 @@ bson_reader_new_from_handle (void *handle, BSON_ASSERT (handle); BSON_ASSERT (rf); - real = bson_malloc0 (sizeof *real); + real = BSON_ALIGNED_ALLOC0 (bson_reader_handle_t); real->type = BSON_READER_HANDLE; real->data = bson_malloc0 (1024); real->handle = handle; @@ -533,7 +533,7 @@ bson_reader_new_from_data (const uint8_t *data, /* IN */ BSON_ASSERT (data); - real = (bson_reader_data_t *) bson_malloc0 (sizeof *real); + real = BSON_ALIGNED_ALLOC0 (bson_reader_data_t); real->type = BSON_READER_DATA; real->data = data; real->length = length; diff --git a/src/bson/bson-string.c b/src/bson/bson-string.c index 68c6ef2..45b71d2 100644 --- a/src/bson/bson-string.c +++ b/src/bson/bson-string.c @@ -90,7 +90,9 @@ bson_string_free (bson_string_t *string, /* IN */ { char *ret = NULL; - BSON_ASSERT (string); + if (!string) { + return NULL; + } if (!free_segment) { ret = string->str; diff --git a/src/bson/bson-timegm-private.h b/src/bson/bson-timegm-private.h index 13ed15d..89d57b5 100644 --- a/src/bson/bson-timegm-private.h +++ b/src/bson/bson-timegm-private.h @@ -29,17 +29,17 @@ BSON_BEGIN_DECLS /* avoid system-dependent struct tm definitions */ struct bson_tm { - int64_t tm_sec; /* seconds after the minute [0-60] */ - int64_t tm_min; /* minutes after the hour [0-59] */ - int64_t tm_hour; /* hours since midnight [0-23] */ - int64_t tm_mday; /* day of the month [1-31] */ - int64_t tm_mon; /* months since January [0-11] */ - int64_t tm_year; /* years since 1900 */ - int64_t tm_wday; /* days since Sunday [0-6] */ - int64_t tm_yday; /* days since January 1 [0-365] */ - int64_t tm_isdst; /* Daylight Savings Time flag */ - int64_t tm_gmtoff; /* offset from CUT in seconds */ - char *tm_zone; /* timezone abbreviation */ + int64_t tm_sec; /* seconds after the minute [0-60] */ + int64_t tm_min; /* minutes after the hour [0-59] */ + int64_t tm_hour; /* hours since midnight [0-23] */ + int64_t tm_mday; /* day of the month [1-31] */ + int64_t tm_mon; /* months since January [0-11] */ + int64_t tm_year; /* years since 1900 */ + int64_t tm_wday; /* days since Sunday [0-6] */ + int64_t tm_yday; /* days since January 1 [0-365] */ + int64_t tm_isdst; /* Daylight Savings Time flag */ + int64_t tm_gmtoff; /* offset from CUT in seconds */ + const char *tm_zone; /* timezone abbreviation */ }; int64_t diff --git a/src/bson/bson-timegm.c b/src/bson/bson-timegm.c index 0f70900..a8f20cf 100644 --- a/src/bson/bson-timegm.c +++ b/src/bson/bson-timegm.c @@ -34,14 +34,13 @@ #if !defined _Noreturn && \ (!defined(__STDC_VERSION__) || __STDC_VERSION__ < 201112) #if 2 < __GNUC__ + (8 <= __GNUC_MINOR__) -#define _Noreturn __attribute__((__noreturn__)) +#define _Noreturn __attribute__ ((__noreturn__)) #else #define _Noreturn #endif #endif -#if (!defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901) && \ - !defined restrict +#if !defined(__STDC_VERSION__) && !defined restrict #define restrict /* empty */ #endif @@ -181,7 +180,7 @@ struct ttinfo { /* time type information */ }; struct lsinfo { /* leap second information */ - int64_t ls_trans; /* transition time */ + int64_t ls_trans; /* transition time */ int_fast64_t ls_corr; /* correction to apply */ }; @@ -226,41 +225,53 @@ struct rule { */ static void -gmtload (struct state *sp); +gmtload (struct state *const sp); static struct bson_tm * -gmtsub (const int64_t *timep, int_fast32_t offset, struct bson_tm *tmp); +gmtsub (const int64_t *const timep, + const int_fast32_t offset, + struct bson_tm *const tmp); static int64_t -increment_overflow (int64_t *number, int64_t delta); +increment_overflow (int64_t *const ip, int64_t j); static int64_t -leaps_thru_end_of (int64_t y) ATTRIBUTE_PURE; +leaps_thru_end_of (const int64_t y) ATTRIBUTE_PURE; static int64_t -increment_overflow32 (int_fast32_t *number, int64_t delta); +increment_overflow32 (int_fast32_t *const lp, int64_t const m); static int64_t -normalize_overflow32 (int_fast32_t *tensptr, int64_t *unitsptr, int64_t base); +normalize_overflow32 (int_fast32_t *const tensptr, + int64_t *const unitsptr, + const int64_t base); static int64_t -normalize_overflow (int64_t *tensptr, int64_t *unitsptr, int64_t base); +normalize_overflow (int64_t *const tensptr, + int64_t *const unitsptr, + const int64_t base); static int64_t -time1 (struct bson_tm *tmp, - struct bson_tm *(*funcp) (const int64_t *, int_fast32_t, struct bson_tm *), - int_fast32_t offset); +time1 (struct bson_tm *const tmp, + struct bson_tm *(*const funcp) (const int64_t *, + int_fast32_t, + struct bson_tm *), + const int_fast32_t offset); static int64_t -time2 (struct bson_tm *tmp, - struct bson_tm *(*funcp) (const int64_t *, int_fast32_t, struct bson_tm *), - int_fast32_t offset, - int64_t *okayp); +time2 (struct bson_tm *const tmp, + struct bson_tm *(*const funcp) (const int64_t *, + int_fast32_t, + struct bson_tm *), + const int_fast32_t offset, + int64_t *const okayp); static int64_t -time2sub (struct bson_tm *tmp, - struct bson_tm *(*funcp) (const int64_t *, int_fast32_t, struct bson_tm *), - int_fast32_t offset, - int64_t *okayp, - int64_t do_norm_secs); +time2sub (struct bson_tm *const tmp, + struct bson_tm *(*const funcp) (const int64_t *, + int_fast32_t, + struct bson_tm *), + const int_fast32_t offset, + int64_t *const okayp, + const int64_t do_norm_secs); static struct bson_tm * -timesub (const int64_t *timep, - int_fast32_t offset, - const struct state *sp, - struct bson_tm *tmp); +timesub (const int64_t *const timep, + const int_fast32_t offset, + const struct state *const sp, + struct bson_tm *const tmp); static int64_t -tmcomp (const struct bson_tm *atmp, const struct bson_tm *btmp); +tmcomp (const struct bson_tm *const atmp, const struct bson_tm *const btmp); static struct state gmtmem; #define gmtptr (&gmtmem) @@ -293,7 +304,7 @@ gmtsub (const int64_t *const timep, const int_fast32_t offset, struct bson_tm *const tmp) { - register struct bson_tm *result; + struct bson_tm *result; if (!gmt_is_set) { gmt_is_set = true; @@ -317,7 +328,7 @@ gmtsub (const int64_t *const timep, */ static int64_t -leaps_thru_end_of (register const int64_t y) +leaps_thru_end_of (const int64_t y) { return (y >= 0) ? (y / 4 - y / 100 + y / 400) : -(leaps_thru_end_of (-(y + 1)) + 1); @@ -326,18 +337,18 @@ leaps_thru_end_of (register const int64_t y) static struct bson_tm * timesub (const int64_t *const timep, const int_fast32_t offset, - register const struct state *const sp, - register struct bson_tm *const tmp) + const struct state *const sp, + struct bson_tm *const tmp) { - register const struct lsinfo *lp; - register int64_t tdays; - register int64_t idays; /* unsigned would be so 2003 */ - register int_fast64_t rem; + const struct lsinfo *lp; + int64_t tdays; + int64_t idays; /* unsigned would be so 2003 */ + int_fast64_t rem; int64_t y; - register const int *ip; - register int_fast64_t corr; - register int64_t hit; - register int64_t i; + const int (*ip)[MONSPERYEAR]; + int_fast64_t corr; + int64_t hit; + int64_t i; corr = 0; hit = 0; @@ -365,9 +376,9 @@ timesub (const int64_t *const timep, rem = *timep - tdays * SECSPERDAY; while (tdays < 0 || tdays >= year_lengths[isleap (y)]) { int64_t newy; - register int64_t tdelta; - register int64_t idelta; - register int64_t leapdays; + int64_t tdelta; + int64_t idelta; + int64_t leapdays; tdelta = tdays / DAYSPERLYEAR; idelta = tdelta; @@ -382,7 +393,7 @@ timesub (const int64_t *const timep, y = newy; } { - register int_fast32_t seconds; + int_fast32_t seconds; seconds = (int_fast32_t) (tdays * SECSPERDAY); tdays = seconds / SECSPERDAY; @@ -433,9 +444,12 @@ timesub (const int64_t *const timep, ** representation. This uses "... ??:59:60" et seq. */ tmp->tm_sec = (int64_t) (rem % SECSPERMIN) + hit; - ip = mon_lengths[isleap (y)]; - for (tmp->tm_mon = 0; idays >= ip[tmp->tm_mon]; ++(tmp->tm_mon)) - idays -= ip[tmp->tm_mon]; + ip = mon_lengths + (isleap (y) ? 1 : 0); + tmp->tm_mon = 0; + while (idays >= (*ip)[tmp->tm_mon]) { + idays -= (*ip)[tmp->tm_mon++]; + BSON_ASSERT (tmp->tm_mon < MONSPERYEAR); + } tmp->tm_mday = (int64_t) (idays + 1); tmp->tm_isdst = 0; #ifdef TM_GMTOFF @@ -464,7 +478,7 @@ timesub (const int64_t *const timep, static int64_t increment_overflow (int64_t *const ip, int64_t j) { - register int64_t const i = *ip; + int64_t const i = *ip; /* ** If i >= 0 there can only be overflow if i + j > INT_MAX @@ -481,18 +495,20 @@ increment_overflow (int64_t *const ip, int64_t j) static int64_t increment_overflow32 (int_fast32_t *const lp, int64_t const m) { - register int_fast32_t const l = *lp; + int_fast32_t const l = *lp; if ((l >= 0) ? (m > INT_FAST32_MAX - l) : (m < INT_FAST32_MIN - l)) return true; - *lp += m; + *lp += (int_fast32_t) m; return false; } static int64_t -normalize_overflow (int64_t *const tensptr, int64_t *const unitsptr, const int64_t base) +normalize_overflow (int64_t *const tensptr, + int64_t *const unitsptr, + const int64_t base) { - register int64_t tensdelta; + int64_t tensdelta; tensdelta = (*unitsptr >= 0) ? (*unitsptr / base) : (-1 - (-1 - *unitsptr) / base); @@ -505,7 +521,7 @@ normalize_overflow32 (int_fast32_t *const tensptr, int64_t *const unitsptr, const int64_t base) { - register int64_t tensdelta; + int64_t tensdelta; tensdelta = (*unitsptr >= 0) ? (*unitsptr / base) : (-1 - (-1 - *unitsptr) / base); @@ -514,10 +530,9 @@ normalize_overflow32 (int_fast32_t *const tensptr, } static int64_t -tmcomp (register const struct bson_tm *const atmp, - register const struct bson_tm *const btmp) +tmcomp (const struct bson_tm *const atmp, const struct bson_tm *const btmp) { - register int64_t result; + int64_t result; if (atmp->tm_year != btmp->tm_year) return atmp->tm_year < btmp->tm_year ? -1 : 1; @@ -531,18 +546,20 @@ tmcomp (register const struct bson_tm *const atmp, static int64_t time2sub (struct bson_tm *const tmp, - struct bson_tm *(*const funcp) (const int64_t *, int_fast32_t, struct bson_tm *), + struct bson_tm *(*const funcp) (const int64_t *, + int_fast32_t, + struct bson_tm *), const int_fast32_t offset, int64_t *const okayp, const int64_t do_norm_secs) { - register const struct state *sp; - register int64_t dir; - register int64_t i, j; - register int64_t saved_seconds; - register int_fast32_t li; - register int64_t lo; - register int64_t hi; + const struct state *sp; + int64_t dir; + int64_t i, j; + int64_t saved_seconds; + int_fast32_t li; + int64_t lo; + int64_t hi; int_fast32_t y; int64_t newt; int64_t t; @@ -700,7 +717,9 @@ time2sub (struct bson_tm *const tmp, static int64_t time2 (struct bson_tm *const tmp, - struct bson_tm *(*const funcp) (const int64_t *, int_fast32_t, struct bson_tm *), + struct bson_tm *(*const funcp) (const int64_t *, + int_fast32_t, + struct bson_tm *), const int_fast32_t offset, int64_t *const okayp) { @@ -717,15 +736,17 @@ time2 (struct bson_tm *const tmp, static int64_t time1 (struct bson_tm *const tmp, - struct bson_tm *(*const funcp) (const int64_t *, int_fast32_t, struct bson_tm *), + struct bson_tm *(*const funcp) (const int64_t *, + int_fast32_t, + struct bson_tm *), const int_fast32_t offset) { - register int64_t t; - register const struct state *sp; - register int64_t samei, otheri; - register int64_t sameind, otherind; - register int64_t i; - register int64_t nseen; + int64_t t; + const struct state *sp; + int64_t samei, otheri; + int64_t sameind, otherind; + int64_t i; + int64_t nseen; int64_t seen[TZ_MAX_TYPES]; int64_t types[TZ_MAX_TYPES]; int64_t okay; @@ -792,4 +813,3 @@ _bson_timegm (struct bson_tm *const tmp) tmp->tm_isdst = 0; return time1 (tmp, gmtsub, 0L); } - diff --git a/src/bson/bson-types.h b/src/bson/bson-types.h index 6a8b1e7..95e3511 100644 --- a/src/bson/bson-types.h +++ b/src/bson/bson-types.h @@ -51,24 +51,21 @@ typedef uint32_t bson_unichar_t; /** - * bson_context_flags_t: - * - * This enumeration is used to configure a bson_context_t. - * - * %BSON_CONTEXT_NONE: Use default options. - * %BSON_CONTEXT_THREAD_SAFE: Context will be called from multiple threads. - * %BSON_CONTEXT_DISABLE_HOST_CACHE: Does nothing, is ignored. - * %BSON_CONTEXT_DISABLE_PID_CACHE: Call getpid() instead of caching the - * result of getpid() when initializing the context. + * @brief Flags configuring the creation of a bson_context_t */ typedef enum { + /** Use default options */ BSON_CONTEXT_NONE = 0, + /* Deprecated: Generating new OIDs from a bson_context_t is always + thread-safe */ BSON_CONTEXT_THREAD_SAFE = (1 << 0), + /* Deprecated: Does nothing and is ignored */ BSON_CONTEXT_DISABLE_HOST_CACHE = (1 << 1), + /* Call getpid() instead of remembering the result of getpid() when using the + context */ BSON_CONTEXT_DISABLE_PID_CACHE = (1 << 2), -#ifdef BSON_HAVE_SYSCALL_TID + /* Deprecated: Does nothing */ BSON_CONTEXT_USE_TASK_ID = (1 << 3), -#endif } bson_context_flags_t; @@ -127,16 +124,14 @@ typedef struct _bson_json_opts_t bson_json_opts_t; * This structure is meant to fit in two sequential 64-byte cachelines. */ #ifdef BSON_MEMCHECK -BSON_ALIGNED_BEGIN (128) -typedef struct _bson_t { +BSON_ALIGNED_BEGIN (128) typedef struct _bson_t { uint32_t flags; /* Internal flags for the bson_t. */ uint32_t len; /* Length of BSON data. */ - char *canary; /* For valgrind check */ + char *canary; /* For leak checks. */ uint8_t padding[120 - sizeof (char *)]; } bson_t BSON_ALIGNED_END (128); #else -BSON_ALIGNED_BEGIN (128) -typedef struct _bson_t { +BSON_ALIGNED_BEGIN (128) typedef struct _bson_t { uint32_t flags; /* Internal flags for the bson_t. */ uint32_t len; /* Length of BSON data. */ uint8_t padding[120]; /* Padding for stack allocation. */ diff --git a/src/bson/bson-version.h b/src/bson/bson-version.h index 74ff9ce..abb1ced 100644 --- a/src/bson/bson-version.h +++ b/src/bson/bson-version.h @@ -37,7 +37,7 @@ * * BSON minor version component (e.g. 2 if %BSON_VERSION is 1.2.3) */ -#define BSON_MINOR_VERSION (20) +#define BSON_MINOR_VERSION (24) /** @@ -45,7 +45,7 @@ * * BSON micro version component (e.g. 3 if %BSON_VERSION is 1.2.3) */ -#define BSON_MICRO_VERSION (0) +#define BSON_MICRO_VERSION (2) /** @@ -60,7 +60,7 @@ * * BSON version. */ -#define BSON_VERSION (1.20.0) +#define BSON_VERSION (1.24.2) /** @@ -69,7 +69,7 @@ * BSON version, encoded as a string, useful for printing and * concatenation. */ -#define BSON_VERSION_S "1.20.0" +#define BSON_VERSION_S "1.24.2" /** @@ -90,7 +90,7 @@ * @micro: required micro version * * Compile-time version checking. Evaluates to %TRUE if the version - * of BSON is greater than the required one. + * of BSON is greater than or equal to the required one. */ #define BSON_CHECK_VERSION(major,minor,micro) \ (BSON_MAJOR_VERSION > (major) || \ diff --git a/src/bson/bson-writer.c b/src/bson/bson-writer.c index d1655e1..5c7c16a 100644 --- a/src/bson/bson-writer.c +++ b/src/bson/bson-writer.c @@ -66,7 +66,7 @@ bson_writer_new (uint8_t **buf, /* IN */ { bson_writer_t *writer; - writer = bson_malloc0 (sizeof *writer); + writer = BSON_ALIGNED_ALLOC0 (bson_writer_t); writer->buf = buf; writer->buflen = buflen; writer->offset = offset; diff --git a/src/bson/bson.c b/src/bson/bson.c index c4b8d4f..1eb63d9 100644 --- a/src/bson/bson.c +++ b/src/bson/bson.c @@ -85,7 +85,8 @@ static char * _bson_as_json_visit_all (const bson_t *bson, size_t *length, bson_json_mode_t mode, - int32_t max_len); + int32_t max_len, + bool is_outermost_array); /* * Globals. @@ -2022,7 +2023,7 @@ bson_new (void) bson_impl_inline_t *impl; bson_t *bson; - bson = bson_malloc (sizeof *bson); + bson = BSON_ALIGNED_ALLOC (bson_t); impl = (bson_impl_inline_t *) bson; impl->flags = BSON_FLAG_INLINE; @@ -2048,8 +2049,10 @@ bson_sized_new (size_t size) BSON_ASSERT (size <= BSON_MAX_SIZE); - b = bson_malloc (sizeof *b); - impl_a = (bson_impl_alloc_t *) b; + { + b = BSON_ALIGNED_ALLOC (bson_t); + impl_a = (bson_impl_alloc_t *) b; + } if (size <= BSON_INLINE_DATA_SIZE) { bson_init (b); @@ -2121,7 +2124,7 @@ bson_new_from_buffer (uint8_t **buf, realloc_func = bson_realloc_ctx; } - bson = bson_malloc0 (sizeof *bson); + bson = BSON_ALIGNED_ALLOC0 (bson_t); impl = (bson_impl_alloc_t *) bson; if (!*buf) { @@ -2520,6 +2523,9 @@ _bson_as_json_visit_utf8 (const bson_iter_t *iter, bson_json_state_t *state = data; char *escaped; + BSON_UNUSED (iter); + BSON_UNUSED (key); + escaped = bson_utf8_escape_for_json (v_utf8, v_utf8_len); if (escaped) { @@ -2542,6 +2548,9 @@ _bson_as_json_visit_int32 (const bson_iter_t *iter, { bson_json_state_t *state = data; + BSON_UNUSED (iter); + BSON_UNUSED (key); + if (state->mode == BSON_JSON_MODE_CANONICAL) { bson_string_append_printf ( state->str, "{ \"$numberInt\" : \"%" PRId32 "\" }", v_int32); @@ -2561,6 +2570,9 @@ _bson_as_json_visit_int64 (const bson_iter_t *iter, { bson_json_state_t *state = data; + BSON_UNUSED (iter); + BSON_UNUSED (key); + if (state->mode == BSON_JSON_MODE_CANONICAL) { bson_string_append_printf ( state->str, "{ \"$numberLong\" : \"%" PRId64 "\" }", v_int64); @@ -2580,6 +2592,10 @@ _bson_as_json_visit_decimal128 (const bson_iter_t *iter, { bson_json_state_t *state = data; char decimal128_string[BSON_DECIMAL128_STRING]; + + BSON_UNUSED (iter); + BSON_UNUSED (key); + bson_decimal128_to_string (value, decimal128_string); bson_string_append (state->str, "{ \"$numberDecimal\" : \""); @@ -2601,6 +2617,9 @@ _bson_as_json_visit_double (const bson_iter_t *iter, uint32_t start_len; bool legacy; + BSON_UNUSED (iter); + BSON_UNUSED (key); + /* Determine if legacy (i.e. unwrapped) output should be used. Relaxed mode * will use this for nan and inf values, which we check manually since old * platforms may not have isinf or isnan. */ @@ -2646,6 +2665,9 @@ _bson_as_json_visit_undefined (const bson_iter_t *iter, { bson_json_state_t *state = data; + BSON_UNUSED (iter); + BSON_UNUSED (key); + bson_string_append (state->str, "{ \"$undefined\" : true }"); return false; @@ -2657,6 +2679,9 @@ _bson_as_json_visit_null (const bson_iter_t *iter, const char *key, void *data) { bson_json_state_t *state = data; + BSON_UNUSED (iter); + BSON_UNUSED (key); + bson_string_append (state->str, "null"); return false; @@ -2672,6 +2697,9 @@ _bson_as_json_visit_oid (const bson_iter_t *iter, bson_json_state_t *state = data; char str[25]; + BSON_UNUSED (iter); + BSON_UNUSED (key); + bson_oid_to_string (oid, str); bson_string_append (state->str, "{ \"$oid\" : \""); bson_string_append (state->str, str); @@ -2693,10 +2721,12 @@ _bson_as_json_visit_binary (const bson_iter_t *iter, size_t b64_len; char *b64; - b64_len = COMMON_PREFIX (bson_b64_ntop_calculate_target_size (v_binary_len)); + BSON_UNUSED (iter); + BSON_UNUSED (key); + + b64_len = mcommon_b64_ntop_calculate_target_size (v_binary_len); b64 = bson_malloc0 (b64_len); - BSON_ASSERT ( - COMMON_PREFIX (bson_b64_ntop (v_binary, v_binary_len, b64, b64_len) != -1)); + BSON_ASSERT (mcommon_b64_ntop (v_binary, v_binary_len, b64, b64_len) != -1); if (state->mode == BSON_JSON_MODE_CANONICAL || state->mode == BSON_JSON_MODE_RELAXED) { @@ -2727,6 +2757,9 @@ _bson_as_json_visit_bool (const bson_iter_t *iter, { bson_json_state_t *state = data; + BSON_UNUSED (iter); + BSON_UNUSED (key); + bson_string_append (state->str, v_bool ? "true" : "false"); return false; @@ -2741,6 +2774,9 @@ _bson_as_json_visit_date_time (const bson_iter_t *iter, { bson_json_state_t *state = data; + BSON_UNUSED (iter); + BSON_UNUSED (key); + if (state->mode == BSON_JSON_MODE_CANONICAL || (state->mode == BSON_JSON_MODE_RELAXED && msec_since_epoch < 0)) { bson_string_append (state->str, "{ \"$date\" : { \"$numberLong\" : \""); @@ -2770,6 +2806,9 @@ _bson_as_json_visit_regex (const bson_iter_t *iter, bson_json_state_t *state = data; char *escaped; + BSON_UNUSED (iter); + BSON_UNUSED (key); + escaped = bson_utf8_escape_for_json (v_regex, -1); if (!escaped) { return true; @@ -2806,6 +2845,9 @@ _bson_as_json_visit_timestamp (const bson_iter_t *iter, { bson_json_state_t *state = data; + BSON_UNUSED (iter); + BSON_UNUSED (key); + bson_string_append (state->str, "{ \"$timestamp\" : { \"t\" : "); bson_string_append_printf (state->str, "%u", v_timestamp); bson_string_append (state->str, ", \"i\" : "); @@ -2828,6 +2870,10 @@ _bson_as_json_visit_dbpointer (const bson_iter_t *iter, char *escaped; char str[25]; + BSON_UNUSED (iter); + BSON_UNUSED (key); + BSON_UNUSED (v_collection_len); + escaped = bson_utf8_escape_for_json (v_collection, -1); if (!escaped) { return true; @@ -2875,6 +2921,9 @@ _bson_as_json_visit_minkey (const bson_iter_t *iter, { bson_json_state_t *state = data; + BSON_UNUSED (iter); + BSON_UNUSED (key); + bson_string_append (state->str, "{ \"$minKey\" : 1 }"); return false; @@ -2888,6 +2937,9 @@ _bson_as_json_visit_maxkey (const bson_iter_t *iter, { bson_json_state_t *state = data; + BSON_UNUSED (iter); + BSON_UNUSED (key); + bson_string_append (state->str, "{ \"$maxKey\" : 1 }"); return false; @@ -2902,6 +2954,8 @@ _bson_as_json_visit_before (const bson_iter_t *iter, bson_json_state_t *state = data; char *escaped; + BSON_UNUSED (iter); + if (state->max_len_reached) { return true; } @@ -2933,16 +2987,20 @@ _bson_as_json_visit_after (const bson_iter_t *iter, const char *key, void *data) { bson_json_state_t *state = data; + BSON_UNUSED (iter); + BSON_UNUSED (key); + if (state->max_len == BSON_MAX_LEN_UNLIMITED) { return false; } - if (state->str->len >= state->max_len) { + if (bson_cmp_greater_equal_us (state->str->len, state->max_len)) { state->max_len_reached = true; - if (state->str->len > state->max_len) { + if (bson_cmp_greater_us (state->str->len, state->max_len)) { + BSON_ASSERT (bson_in_range_signed (uint32_t, state->max_len)); /* Truncate string to maximum length */ - bson_string_truncate (state->str, state->max_len); + bson_string_truncate (state->str, (uint32_t) state->max_len); } return true; @@ -2969,6 +3027,9 @@ _bson_as_json_visit_code (const bson_iter_t *iter, bson_json_state_t *state = data; char *escaped; + BSON_UNUSED (iter); + BSON_UNUSED (key); + escaped = bson_utf8_escape_for_json (v_code, v_code_len); if (!escaped) { return true; @@ -2993,6 +3054,9 @@ _bson_as_json_visit_symbol (const bson_iter_t *iter, bson_json_state_t *state = data; char *escaped; + BSON_UNUSED (iter); + BSON_UNUSED (key); + escaped = bson_utf8_escape_for_json (v_symbol, v_symbol_len); if (!escaped) { return true; @@ -3028,6 +3092,9 @@ _bson_as_json_visit_codewscope (const bson_iter_t *iter, char *scope; int32_t max_scope_len = BSON_MAX_LEN_UNLIMITED; + BSON_UNUSED (iter); + BSON_UNUSED (key); + code_escaped = bson_utf8_escape_for_json (v_code, v_code_len); if (!code_escaped) { return true; @@ -3041,10 +3108,12 @@ _bson_as_json_visit_codewscope (const bson_iter_t *iter, /* Encode scope with the same mode */ if (state->max_len != BSON_MAX_LEN_UNLIMITED) { - max_scope_len = BSON_MAX (0, state->max_len - state->str->len); + BSON_ASSERT (bson_in_range_unsigned (int32_t, state->str->len)); + max_scope_len = BSON_MAX (0, state->max_len - (int32_t) state->str->len); } - scope = _bson_as_json_visit_all (v_scope, NULL, state->mode, max_scope_len); + scope = _bson_as_json_visit_all ( + v_scope, NULL, state->mode, max_scope_len, false); if (!scope) { return true; @@ -3086,6 +3155,9 @@ _bson_as_json_visit_document (const bson_iter_t *iter, bson_json_state_t child_state = {0, true, state->err_offset}; bson_iter_t child; + BSON_UNUSED (iter); + BSON_UNUSED (key); + if (state->depth >= BSON_MAX_RECURSION) { bson_string_append (state->str, "{ ... }"); return false; @@ -3097,7 +3169,9 @@ _bson_as_json_visit_document (const bson_iter_t *iter, child_state.mode = state->mode; child_state.max_len = BSON_MAX_LEN_UNLIMITED; if (state->max_len != BSON_MAX_LEN_UNLIMITED) { - child_state.max_len = BSON_MAX (0, state->max_len - state->str->len); + BSON_ASSERT (bson_in_range_unsigned (int32_t, state->str->len)); + child_state.max_len = + BSON_MAX (0, state->max_len - (int32_t) state->str->len); } child_state.max_len_reached = child_state.max_len == 0; @@ -3134,6 +3208,9 @@ _bson_as_json_visit_array (const bson_iter_t *iter, bson_json_state_t child_state = {0, false, state->err_offset}; bson_iter_t child; + BSON_UNUSED (iter); + BSON_UNUSED (key); + if (state->depth >= BSON_MAX_RECURSION) { bson_string_append (state->str, "{ ... }"); return false; @@ -3145,7 +3222,9 @@ _bson_as_json_visit_array (const bson_iter_t *iter, child_state.mode = state->mode; child_state.max_len = BSON_MAX_LEN_UNLIMITED; if (state->max_len != BSON_MAX_LEN_UNLIMITED) { - child_state.max_len = BSON_MAX (0, state->max_len - state->str->len); + BSON_ASSERT (bson_in_range_unsigned (int32_t, state->str->len)); + child_state.max_len = + BSON_MAX (0, state->max_len - (int32_t) state->str->len); } child_state.max_len_reached = child_state.max_len == 0; @@ -3176,7 +3255,8 @@ static char * _bson_as_json_visit_all (const bson_t *bson, size_t *length, bson_json_mode_t mode, - int32_t max_len) + int32_t max_len, + bool is_outermost_array) { bson_json_state_t state; bson_iter_t iter; @@ -3194,7 +3274,7 @@ _bson_as_json_visit_all (const bson_t *bson, *length = 3; } - return bson_strdup ("{ }"); + return bson_strdup (is_outermost_array ? "[ ]" : "{ }"); } if (!bson_iter_init (&iter, bson)) { @@ -3202,8 +3282,8 @@ _bson_as_json_visit_all (const bson_t *bson, } state.count = 0; - state.keys = true; - state.str = bson_string_new ("{ "); + state.keys = !is_outermost_array; + state.str = bson_string_new (is_outermost_array ? "[ " : "{ "); state.depth = 0; state.err_offset = &err_offset; state.mode = mode; @@ -3223,11 +3303,11 @@ _bson_as_json_visit_all (const bson_t *bson, return NULL; } - /* Append closing space and } separately, in case we hit the max in between. */ + /* Append closing space and } separately, in case we hit the max in between. + */ remaining = state.max_len - state.str->len; - if (state.max_len == BSON_MAX_LEN_UNLIMITED || - remaining > 1) { - bson_string_append (state.str, " }"); + if (state.max_len == BSON_MAX_LEN_UNLIMITED || remaining > 1) { + bson_string_append (state.str, is_outermost_array ? " ]" : " }"); } else if (remaining == 1) { bson_string_append (state.str, " "); } @@ -3245,15 +3325,16 @@ bson_as_json_with_opts (const bson_t *bson, size_t *length, const bson_json_opts_t *opts) { - return _bson_as_json_visit_all (bson, length, opts->mode, opts->max_len); + return _bson_as_json_visit_all ( + bson, length, opts->mode, opts->max_len, opts->is_outermost_array); } char * bson_as_canonical_extended_json (const bson_t *bson, size_t *length) { - const bson_json_opts_t opts = {BSON_JSON_MODE_CANONICAL, - BSON_MAX_LEN_UNLIMITED}; + const bson_json_opts_t opts = { + BSON_JSON_MODE_CANONICAL, BSON_MAX_LEN_UNLIMITED, false}; return bson_as_json_with_opts (bson, length, &opts); } @@ -3261,8 +3342,8 @@ bson_as_canonical_extended_json (const bson_t *bson, size_t *length) char * bson_as_json (const bson_t *bson, size_t *length) { - const bson_json_opts_t opts = {BSON_JSON_MODE_LEGACY, - BSON_MAX_LEN_UNLIMITED}; + const bson_json_opts_t opts = { + BSON_JSON_MODE_LEGACY, BSON_MAX_LEN_UNLIMITED, false}; return bson_as_json_with_opts (bson, length, &opts); } @@ -3270,8 +3351,8 @@ bson_as_json (const bson_t *bson, size_t *length) char * bson_as_relaxed_extended_json (const bson_t *bson, size_t *length) { - const bson_json_opts_t opts = {BSON_JSON_MODE_RELAXED, - BSON_MAX_LEN_UNLIMITED}; + const bson_json_opts_t opts = { + BSON_JSON_MODE_RELAXED, BSON_MAX_LEN_UNLIMITED, false}; return bson_as_json_with_opts (bson, length, &opts); } @@ -3279,57 +3360,27 @@ bson_as_relaxed_extended_json (const bson_t *bson, size_t *length) char * bson_array_as_json (const bson_t *bson, size_t *length) { - bson_json_state_t state; - bson_iter_t iter; - ssize_t err_offset = -1; - - BSON_ASSERT (bson); - - if (length) { - *length = 0; - } - - if (bson_empty0 (bson)) { - if (length) { - *length = 3; - } - - return bson_strdup ("[ ]"); - } - - if (!bson_iter_init (&iter, bson)) { - return NULL; - } - - state.count = 0; - state.keys = false; - state.str = bson_string_new ("[ "); - state.depth = 0; - state.err_offset = &err_offset; - state.mode = BSON_JSON_MODE_LEGACY; - state.max_len = BSON_MAX_LEN_UNLIMITED; - state.max_len_reached = false; + const bson_json_opts_t opts = { + BSON_JSON_MODE_LEGACY, BSON_MAX_LEN_UNLIMITED, true}; + return bson_as_json_with_opts (bson, length, &opts); +} - if ((bson_iter_visit_all (&iter, &bson_as_json_visitors, &state) || - err_offset != -1) && - !state.max_len_reached) { - /* - * We were prematurely exited due to corruption or failed visitor. - */ - bson_string_free (state.str, true); - if (length) { - *length = 0; - } - return NULL; - } - bson_string_append (state.str, " ]"); +char * +bson_array_as_relaxed_extended_json (const bson_t *bson, size_t *length) +{ + const bson_json_opts_t opts = { + BSON_JSON_MODE_RELAXED, BSON_MAX_LEN_UNLIMITED, true}; + return bson_as_json_with_opts (bson, length, &opts); +} - if (length) { - *length = state.str->len; - } - return bson_string_free (state.str, false); +char * +bson_array_as_canonical_extended_json (const bson_t *bson, size_t *length) +{ + const bson_json_opts_t opts = { + BSON_JSON_MODE_CANONICAL, BSON_MAX_LEN_UNLIMITED, true}; + return bson_as_json_with_opts (bson, length, &opts); } @@ -3449,6 +3500,10 @@ _bson_iter_validate_codewscope (const bson_iter_t *iter, bson_validate_state_t *state = data; size_t offset = 0; + BSON_UNUSED (key); + BSON_UNUSED (v_code_len); + BSON_UNUSED (v_code); + if (!bson_validate (v_scope, state->flags, &offset)) { state->err_offset = iter->off + offset; VALIDATION_ERR (BSON_VALIDATE_NONE, "%s", "corrupt code-with-scope"); @@ -3498,6 +3553,8 @@ _bson_iter_validate_document (const bson_iter_t *iter, bson_iter_t child; bson_validate_phase_t phase = state->phase; + BSON_UNUSED (key); + if (!bson_iter_init (&child, v_document)) { state->err_offset = iter->off; return true; diff --git a/src/bson/bson.h b/src/bson/bson.h index 6a5006f..83b6f02 100644 --- a/src/bson/bson.h +++ b/src/bson/bson.h @@ -28,6 +28,7 @@ #include "bson-macros.h" #include "bson-config.h" #include "bson-atomic.h" +#include "bson-cmp.h" #include "bson-context.h" #include "bson-clock.h" #include "bson-decimal128.h" @@ -506,7 +507,7 @@ bson_validate_with_error (const bson_t *bson, * The caller is responsible for freeing the resulting string. If @length is * non-NULL, then the length of the resulting string will be placed in @length. * - * See http://docs.mongodb.org/manual/reference/mongodb-extended-json/ for + * See https://www.mongodb.com/docs/manual/reference/mongodb-extended-json/ for * more information on extended JSON. * * Returns: A newly allocated string that should be freed with bson_free(). @@ -530,7 +531,7 @@ bson_as_json_with_opts (const bson_t *bson, * The caller is responsible for freeing the resulting string. If @length is * non-NULL, then the length of the resulting string will be placed in @length. * - * See http://docs.mongodb.org/manual/reference/mongodb-extended-json/ for + * See https://www.mongodb.com/docs/manual/reference/mongodb-extended-json/ for * more information on extended JSON. * * Returns: A newly allocated string that should be freed with bson_free(). @@ -569,7 +570,7 @@ bson_as_json (const bson_t *bson, size_t *length); * The caller is responsible for freeing the resulting string. If @length is * non-NULL, then the length of the resulting string will be placed in @length. * - * See http://docs.mongodb.org/manual/reference/mongodb-extended-json/ for + * See https://www.mongodb.com/docs/manual/reference/mongodb-extended-json/ for * more information on extended JSON. * * Returns: A newly allocated string that should be freed with bson_free(). @@ -579,10 +580,18 @@ bson_as_relaxed_extended_json (const bson_t *bson, size_t *length); /* like bson_as_json() but for outermost arrays. */ +BSON_EXPORT (char *) bson_array_as_json (const bson_t *bson, size_t *length); + + +/* like bson_as_relaxed_extended_json() but for outermost arrays. */ BSON_EXPORT (char *) -bson_array_as_json (const bson_t *bson, size_t *length); +bson_array_as_relaxed_extended_json (const bson_t *bson, size_t *length); +/* like bson_as_canonical_extended_json() but for outermost arrays. */ +BSON_EXPORT (char *) +bson_array_as_canonical_extended_json (const bson_t *bson, size_t *length); + BSON_EXPORT (bool) bson_append_value (bson_t *bson, const char *key, diff --git a/src/common/common-b64-private.h b/src/common/common-b64-private.h index e3959d3..531cfc5 100644 --- a/src/common/common-b64-private.h +++ b/src/common/common-b64-private.h @@ -21,36 +21,43 @@ #include -/* When encoding from "network" (raw data) to "presentation" (base64 encoded). +#define mcommon_b64_ntop_calculate_target_size \ + COMMON_NAME (b64_ntop_calculate_target_size) +#define mcommon_b64_pton_calculate_target_size \ + COMMON_NAME (b64_pton_calculate_target_size) +#define mcommon_b64_ntop COMMON_NAME (b64_ntop) +#define mcommon_b64_pton COMMON_NAME (b64_pton) + +/** + * When encoding from "network" (raw data) to "presentation" (base64 encoded). * Includes the trailing null byte. */ -size_t COMMON_PREFIX (bson_b64_ntop_calculate_target_size) (size_t raw_size); +size_t +mcommon_b64_ntop_calculate_target_size (size_t raw_size); /* When encoding from "presentation" (base64 encoded) to "network" (raw data). * This may be an overestimate if the base64 data includes spaces. For a more - * accurate size, call bson_b64_pton (src, NULL, 0), which will read the src + * accurate size, call b64_pton (src, NULL, 0), which will read the src * data and return an exact size. */ -size_t COMMON_PREFIX (bson_b64_pton_calculate_target_size) ( - size_t base64_encoded_size); +size_t +mcommon_b64_pton_calculate_target_size (size_t base64_encoded_size); /* Returns the number of bytes written (excluding NULL byte) to target on * success or -1 on error. Adds a trailing NULL byte. * Encodes from "network" (raw data) to "presentation" (base64 encoded), * hence the obscure name "ntop". */ -int COMMON_PREFIX (bson_b64_ntop) (uint8_t const *src, - size_t srclength, - char *target, - size_t targsize); - -/* If target is not NULL, the number of bytes written to target on success or -1 - * on error. - * If target is NULL, returns the exact number of bytes that would be - * written to target on decoding. - * Encodes from "presentation" (base64 encoded) to "network" (raw data), - * hence the obscure name "pton". +int +mcommon_b64_ntop (uint8_t const *src, + size_t srclength, + char *target, + size_t targsize); + +/** If target is not NULL, the number of bytes written to target on success or + * -1 on error. If target is NULL, returns the exact number of bytes that would + * be written to target on decoding. Encodes from "presentation" (base64 + * encoded) to "network" (raw data), hence the obscure name "pton". */ -int COMMON_PREFIX (bson_b64_pton) (char const *src, - uint8_t *target, - size_t targsize); +int +mcommon_b64_pton (char const *src, uint8_t *target, size_t targsize); #endif /* COMMON_B64_PRIVATE_H */ diff --git a/src/common/common-b64.c b/src/common/common-b64.c index 552d0e5..71e197f 100644 --- a/src/common/common-b64.c +++ b/src/common/common-b64.c @@ -113,10 +113,11 @@ static const char Pad64 = '='; * characters followed by one "=" padding character. */ -int COMMON_PREFIX (bson_b64_ntop) (uint8_t const *src, - size_t srclength, - char *target, - size_t targsize) +int +mcommon_b64_ntop (uint8_t const *src, + size_t srclength, + char *target, + size_t targsize) { size_t datalength = 0; uint8_t input[3]; @@ -517,9 +518,8 @@ mongoc_b64_pton_len (char const *src) } -int COMMON_PREFIX (bson_b64_pton) (char const *src, - uint8_t *target, - size_t targsize) +int +mcommon_b64_pton (char const *src, uint8_t *target, size_t targsize) { static mongoc_common_once_t once = MONGOC_COMMON_ONCE_INIT; @@ -535,7 +535,8 @@ int COMMON_PREFIX (bson_b64_pton) (char const *src, return mongoc_b64_pton_len (src); } -size_t COMMON_PREFIX (bson_b64_ntop_calculate_target_size) (size_t raw_size) +size_t +mcommon_b64_ntop_calculate_target_size (size_t raw_size) { size_t num_bits = raw_size * 8; /* Calculate how many groups of six bits this contains, adding 5 to round up @@ -547,8 +548,8 @@ size_t COMMON_PREFIX (bson_b64_ntop_calculate_target_size) (size_t raw_size) return num_b64_chars_with_padding + 1; } -size_t COMMON_PREFIX (bson_b64_pton_calculate_target_size) ( - size_t base64_encoded_size) +size_t +mcommon_b64_pton_calculate_target_size (size_t base64_encoded_size) { /* Without inspecting the data, we don't know how many padding characters * there are. Assuming none, that means each character represents 6 bits of diff --git a/src/common/common-md5-private.h b/src/common/common-md5-private.h index b2d164a..4feadb9 100644 --- a/src/common/common-md5-private.h +++ b/src/common/common-md5-private.h @@ -23,11 +23,16 @@ BSON_BEGIN_DECLS -void COMMON_PREFIX (_bson_md5_init) (bson_md5_t *pms); -void COMMON_PREFIX (_bson_md5_append) (bson_md5_t *pms, - const uint8_t *data, - uint32_t nbytes); -void COMMON_PREFIX (_bson_md5_finish) (bson_md5_t *pms, uint8_t digest[16]); +#define mcommon_md5_init COMMON_NAME (md5_init) +#define mcommon_md5_append COMMON_NAME (md5_append) +#define mcommon_md5_finish COMMON_NAME (md5_finish) + +void +mcommon_md5_init (bson_md5_t *pms); +void +mcommon_md5_append (bson_md5_t *pms, const uint8_t *data, uint32_t nbytes); +void +mcommon_md5_finish (bson_md5_t *pms, uint8_t digest[16]); BSON_END_DECLS diff --git a/src/common/common-md5.c b/src/common/common-md5.c index 1325453..c726b91 100644 --- a/src/common/common-md5.c +++ b/src/common/common-md5.c @@ -324,7 +324,8 @@ bson_md5_process (bson_md5_t *md5, const uint8_t *data) md5->abcd[3] += d; } -void COMMON_PREFIX (_bson_md5_init) (bson_md5_t *pms) +void +mcommon_md5_init (bson_md5_t *pms) { pms->count[0] = pms->count[1] = 0; pms->abcd[0] = 0x67452301; @@ -333,9 +334,8 @@ void COMMON_PREFIX (_bson_md5_init) (bson_md5_t *pms) pms->abcd[3] = 0x10325476; } -void COMMON_PREFIX (_bson_md5_append) (bson_md5_t *pms, - const uint8_t *data, - uint32_t nbytes) +void +mcommon_md5_append (bson_md5_t *pms, const uint8_t *data, uint32_t nbytes) { const uint8_t *p = data; int left = nbytes; @@ -373,7 +373,8 @@ void COMMON_PREFIX (_bson_md5_append) (bson_md5_t *pms, } -void COMMON_PREFIX (_bson_md5_finish) (bson_md5_t *pms, uint8_t digest[16]) +void +mcommon_md5_finish (bson_md5_t *pms, uint8_t digest[16]) { static const uint8_t pad[64] = { 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -386,10 +387,9 @@ void COMMON_PREFIX (_bson_md5_finish) (bson_md5_t *pms, uint8_t digest[16]) for (i = 0; i < 8; ++i) data[i] = (uint8_t) (pms->count[i >> 2] >> ((i & 3) << 3)); /* Pad to 56 bytes mod 64. */ - COMMON_PREFIX (_bson_md5_append) - (pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1); + mcommon_md5_append (pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1); /* Append the length. */ - COMMON_PREFIX (_bson_md5_append) (pms, data, sizeof (data)); + mcommon_md5_append (pms, data, sizeof (data)); for (i = 0; i < 16; ++i) digest[i] = (uint8_t) (pms->abcd[i >> 2] >> ((i & 3) << 3)); } diff --git a/src/common/common-prelude.h b/src/common/common-prelude.h index 42f0afa..efc18bc 100644 --- a/src/common/common-prelude.h +++ b/src/common/common-prelude.h @@ -19,9 +19,10 @@ #error "Only or can be included directly." #endif -#ifndef COMMON_PREFIX_ -#define COMMON_PREFIX_ +#define COMMON_NAME_1(a, b) a##_##b + +#if defined(MCOMMON_NAME_PREFIX) && !defined(__INTELLISENSE__) +#define COMMON_NAME(Name) COMMON_NAME_1 (MCOMMON_NAME_PREFIX, Name) +#else +#define COMMON_NAME(Name) COMMON_NAME_1 (mcommon, Name) #endif -#define JOINER(x, y) x##_##y -#define NAME_EVALUATOR(x, y) JOINER (x, y) -#define COMMON_PREFIX(name) NAME_EVALUATOR (COMMON_PREFIX_, name) diff --git a/src/common/common-thread-private.h b/src/common/common-thread-private.h index 8d560f2..8aecbff 100644 --- a/src/common/common-thread-private.h +++ b/src/common/common-thread-private.h @@ -29,6 +29,9 @@ BSON_BEGIN_DECLS +#define mcommon_thread_create COMMON_NAME (thread_create) +#define mcommon_thread_join COMMON_NAME (thread_join) + #if defined(BSON_OS_UNIX) #include @@ -114,13 +117,20 @@ typedef struct { /* Functions that require definitions get the common prefix (_mongoc for * libmongoc or _bson for libbson) to avoid duplicate symbols when linking both * libbson and libmongoc statically. */ -int COMMON_PREFIX (thread_join) (bson_thread_t thread); -int COMMON_PREFIX (thread_create) (bson_thread_t *thread, - BSON_THREAD_FUN_TYPE (func), - void *arg); +int +mcommon_thread_join (bson_thread_t thread); +// mcommon_thread_create returns 0 on success. Returns a non-zero error code on +// error. Callers may use `bson_strerror_r` to get an error message from the +// returned error code. +int +mcommon_thread_create (bson_thread_t *thread, + BSON_THREAD_FUN_TYPE (func), + void *arg); #if defined(MONGOC_ENABLE_DEBUG_ASSERTIONS) && defined(BSON_OS_UNIX) -bool COMMON_PREFIX (mutex_is_locked) (bson_mutex_t *mutex); +#define mcommon_mutex_is_locked COMMON_NAME (mutex_is_locked) +bool +mcommon_mutex_is_locked (bson_mutex_t *mutex); #endif /** diff --git a/src/common/common-thread.c b/src/common/common-thread.c index aca86dc..037a649 100644 --- a/src/common/common-thread.c +++ b/src/common/common-thread.c @@ -16,20 +16,28 @@ #include "common-thread-private.h" +#include + #if defined(BSON_OS_UNIX) -int COMMON_PREFIX (thread_create) (bson_thread_t *thread, - BSON_THREAD_FUN_TYPE (func), - void *arg) +int +mcommon_thread_create (bson_thread_t *thread, + BSON_THREAD_FUN_TYPE (func), + void *arg) { + BSON_ASSERT_PARAM (thread); + BSON_ASSERT_PARAM (func); + BSON_ASSERT (arg || true); // optional. return pthread_create (thread, NULL, func, arg); } -int COMMON_PREFIX (thread_join) (bson_thread_t thread) +int +mcommon_thread_join (bson_thread_t thread) { return pthread_join (thread, NULL); } #if defined(MONGOC_ENABLE_DEBUG_ASSERTIONS) && defined(BSON_OS_UNIX) -bool COMMON_PREFIX (mutex_is_locked) (bson_mutex_t *mutex) +bool +mcommon_mutex_is_locked (bson_mutex_t *mutex) { return mutex->valid_tid && pthread_equal (pthread_self (), mutex->lock_owner); @@ -37,17 +45,23 @@ bool COMMON_PREFIX (mutex_is_locked) (bson_mutex_t *mutex) #endif #else -int COMMON_PREFIX (thread_create) (bson_thread_t *thread, - BSON_THREAD_FUN_TYPE (func), - void *arg) +int +mcommon_thread_create (bson_thread_t *thread, + BSON_THREAD_FUN_TYPE (func), + void *arg) { + BSON_ASSERT_PARAM (thread); + BSON_ASSERT_PARAM (func); + BSON_ASSERT (arg || true); // optional. + *thread = (HANDLE) _beginthreadex (NULL, 0, func, arg, 0, NULL); if (0 == *thread) { - return 1; + return errno; } return 0; } -int COMMON_PREFIX (thread_join) (bson_thread_t thread) +int +mcommon_thread_join (bson_thread_t thread) { int ret; diff --git a/src/jsonsl/jsonsl.c b/src/jsonsl/jsonsl.c index dddd973..a7bb8f4 100644 --- a/src/jsonsl/jsonsl.c +++ b/src/jsonsl/jsonsl.c @@ -457,13 +457,23 @@ jsonsl_feed(jsonsl_t jsn, const jsonsl_char_t *bytes, size_t nbytes) } else if (state->special_flags & JSONSL_SPECIALf_NULL || state->special_flags & JSONSL_SPECIALf_NAN) { /* previous char was "n", are we parsing null or nan? */ - if (CUR_CHAR != 'u') { + const bool not_u = CUR_CHAR != 'u'; + const bool not_a = tolower (CUR_CHAR) != 'a'; + if (not_u) { state->special_flags &= ~JSONSL_SPECIALf_NULL; } - - if (tolower(CUR_CHAR) != 'a') { + if (not_a) { state->special_flags &= ~JSONSL_SPECIALf_NAN; } + if (not_u && not_a) { + /* This verify will always fail, as we have an 'n' + * followed by a character that is neither 'a' nor 'u' + * (and hence cannot be "null"). The purpose of this + * VERIFY_SPECIAL is to generate an error in tokenization + * that stops if a bare 'n' cannot possibly be a "nan" or + * a "null". */ + VERIFY_SPECIAL ("null", 4); + } #endif } INCR_METRIC(SPECIAL_FASTPATH); diff --git a/vendor.sh b/vendor.sh index b28bfc5..c87032d 100644 --- a/vendor.sh +++ b/vendor.sh @@ -3,7 +3,7 @@ cd .. git clone git@github.com:mongodb/mongo-c-driver.git cd mongo-c-driver git clean -xdf -git checkout 1.20.0 +git checkout 1.24.2 python build/calc_release_version.py > VERSION_CURRENT mkdir cmake-build && cd cmake-build cmake -DENABLE_AUTOMATIC_INIT_AND_CLEANUP=OFF -DENABLE_MONGOC=OFF .. @@ -19,4 +19,9 @@ rsync -r mongo-c-driver/src/common/*.[hc] python-bsonjs/src/common/ rsync -r mongo-c-driver/cmake-build/src/common/*.[hc] python-bsonjs/src/common/ rsync -r mongo-c-driver/cmake-build/src/libbson/src/bson/*.[hc] python-bsonjs/src/bson/ -rsync -r mongo-c-driver/cmake-build/src/libbson/src/jsonsl/*.[hc] python-bsonjs/src/jsonsl/ \ No newline at end of file + +# Ignore autogenerated bson-config.h +cd python-bsonjs/ +git diff -- src/bson/bson-config.h | tee +echo "**** Review libbson's autogenerated src/bson/bson-config.h (above) for newly added (or removed) macros ****" +git checkout -- src/bson/bson-config.h